Skip to content

Commit 1a6fafe

Browse files
authored
[PR #10037/2e369db backport][3.11] Refactor requests and responses to use classvar defaults to avoid multiple __init__s (#10053)
1 parent 653302e commit 1a6fafe

File tree

6 files changed

+43
-48
lines changed

6 files changed

+43
-48
lines changed

CHANGES/10037.misc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved performances of creating objects during the HTTP request lifecycle -- by :user:`bdraco`.

aiohttp/helpers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,10 +694,11 @@ def ceil_timeout(
694694

695695

696696
class HeadersMixin:
697+
"""Mixin for handling headers."""
698+
697699
ATTRS = frozenset(["_content_type", "_content_dict", "_stored_content_type"])
698700

699701
_headers: MultiMapping[str]
700-
701702
_content_type: Optional[str] = None
702703
_content_dict: Optional[Dict[str, str]] = None
703704
_stored_content_type: Union[str, None, _SENTINEL] = sentinel

aiohttp/web_app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,8 @@ def _make_request(
498498
task: "asyncio.Task[None]",
499499
_cls: Type[Request] = Request,
500500
) -> Request:
501+
if TYPE_CHECKING:
502+
assert self._loop is not None
501503
return _cls(
502504
message,
503505
payload,

aiohttp/web_request.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ class BaseRequest(MutableMapping[str, Any], HeadersMixin):
146146
"_transport_peername",
147147
]
148148
)
149+
_post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None
150+
_read_bytes: Optional[bytes] = None
149151

150152
def __init__(
151153
self,
@@ -162,8 +164,6 @@ def __init__(
162164
host: Optional[str] = None,
163165
remote: Optional[str] = None,
164166
) -> None:
165-
if state is None:
166-
state = {}
167167
self._message = message
168168
self._protocol = protocol
169169
self._payload_writer = payload_writer
@@ -187,20 +187,18 @@ def __init__(
187187
self._cache["scheme"] = url.scheme
188188
self._rel_url = url.relative()
189189
else:
190-
self._rel_url = message.url
190+
self._rel_url = url
191191
if scheme is not None:
192192
self._cache["scheme"] = scheme
193193
if host is not None:
194194
self._cache["host"] = host
195-
self._post: Optional[MultiDictProxy[Union[str, bytes, FileField]]] = None
196-
self._read_bytes: Optional[bytes] = None
197195

198-
self._state = state
196+
self._state = {} if state is None else state
199197
self._task = task
200198
self._client_max_size = client_max_size
201199
self._loop = loop
202200

203-
transport = self._protocol.transport
201+
transport = protocol.transport
204202
assert transport is not None
205203
self._transport_sslcontext = transport.get_extra_info("sslcontext")
206204
self._transport_peername = transport.get_extra_info("peername")
@@ -847,14 +845,7 @@ class Request(BaseRequest):
847845

848846
ATTRS = BaseRequest.ATTRS | frozenset(["_match_info"])
849847

850-
def __init__(self, *args: Any, **kwargs: Any) -> None:
851-
super().__init__(*args, **kwargs)
852-
853-
# matchdict, route_name, handler
854-
# or information about traversal lookup
855-
856-
# initialized after route resolving
857-
self._match_info: Optional[UrlMappingMatchInfo] = None
848+
_match_info: Optional["UrlMappingMatchInfo"] = None
858849

859850
if DEBUG:
860851

aiohttp/web_response.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,20 @@ class ContentCoding(enum.Enum):
7676

7777
class StreamResponse(BaseClass, HeadersMixin):
7878

79-
_length_check = True
80-
8179
_body: Union[None, bytes, bytearray, Payload]
80+
_length_check = True
81+
_body = None
82+
_keep_alive: Optional[bool] = None
83+
_chunked: bool = False
84+
_compression: bool = False
85+
_compression_strategy: int = zlib.Z_DEFAULT_STRATEGY
86+
_compression_force: Optional[ContentCoding] = None
87+
_req: Optional["BaseRequest"] = None
88+
_payload_writer: Optional[AbstractStreamWriter] = None
89+
_eof_sent: bool = False
90+
_must_be_empty_body: Optional[bool] = None
91+
_body_length = 0
92+
_cookies: Optional[SimpleCookie] = None
8293

8394
def __init__(
8495
self,
@@ -95,19 +106,6 @@ def __init__(
95106
the headers when creating a new response object. It is not intended
96107
to be used by external code.
97108
"""
98-
self._body = None
99-
self._keep_alive: Optional[bool] = None
100-
self._chunked = False
101-
self._compression = False
102-
self._compression_strategy: int = zlib.Z_DEFAULT_STRATEGY
103-
self._compression_force: Optional[ContentCoding] = None
104-
self._cookies: Optional[SimpleCookie] = None
105-
106-
self._req: Optional[BaseRequest] = None
107-
self._payload_writer: Optional[AbstractStreamWriter] = None
108-
self._eof_sent = False
109-
self._must_be_empty_body: Optional[bool] = None
110-
self._body_length = 0
111109
self._state: Dict[str, Any] = {}
112110

113111
if _real_headers is not None:
@@ -613,6 +611,9 @@ def __eq__(self, other: object) -> bool:
613611

614612

615613
class Response(StreamResponse):
614+
615+
_compressed_body: Optional[bytes] = None
616+
616617
def __init__(
617618
self,
618619
*,
@@ -677,7 +678,6 @@ def __init__(
677678
else:
678679
self.body = body
679680

680-
self._compressed_body: Optional[bytes] = None
681681
self._zlib_executor_size = zlib_executor_size
682682
self._zlib_executor = zlib_executor
683683

aiohttp/web_ws.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,22 @@ def __bool__(self) -> bool:
6161

6262
class WebSocketResponse(StreamResponse):
6363

64-
_length_check = False
64+
_length_check: bool = False
65+
_ws_protocol: Optional[str] = None
66+
_writer: Optional[WebSocketWriter] = None
67+
_reader: Optional[WebSocketDataQueue] = None
68+
_closed: bool = False
69+
_closing: bool = False
70+
_conn_lost: int = 0
71+
_close_code: Optional[int] = None
72+
_loop: Optional[asyncio.AbstractEventLoop] = None
73+
_waiting: bool = False
74+
_close_wait: Optional[asyncio.Future[None]] = None
75+
_exception: Optional[BaseException] = None
76+
_heartbeat_when: float = 0.0
77+
_heartbeat_cb: Optional[asyncio.TimerHandle] = None
78+
_pong_response_cb: Optional[asyncio.TimerHandle] = None
79+
_ping_task: Optional[asyncio.Task[None]] = None
6580

6681
def __init__(
6782
self,
@@ -78,30 +93,15 @@ def __init__(
7893
) -> None:
7994
super().__init__(status=101)
8095
self._protocols = protocols
81-
self._ws_protocol: Optional[str] = None
82-
self._writer: Optional[WebSocketWriter] = None
83-
self._reader: Optional[WebSocketDataQueue] = None
84-
self._closed = False
85-
self._closing = False
86-
self._conn_lost = 0
87-
self._close_code: Optional[int] = None
88-
self._loop: Optional[asyncio.AbstractEventLoop] = None
89-
self._waiting: bool = False
90-
self._close_wait: Optional[asyncio.Future[None]] = None
91-
self._exception: Optional[BaseException] = None
9296
self._timeout = timeout
9397
self._receive_timeout = receive_timeout
9498
self._autoclose = autoclose
9599
self._autoping = autoping
96100
self._heartbeat = heartbeat
97-
self._heartbeat_when = 0.0
98-
self._heartbeat_cb: Optional[asyncio.TimerHandle] = None
99101
if heartbeat is not None:
100102
self._pong_heartbeat = heartbeat / 2.0
101-
self._pong_response_cb: Optional[asyncio.TimerHandle] = None
102103
self._compress: Union[bool, int] = compress
103104
self._max_msg_size = max_msg_size
104-
self._ping_task: Optional[asyncio.Task[None]] = None
105105
self._writer_limit = writer_limit
106106

107107
def _cancel_heartbeat(self) -> None:

0 commit comments

Comments
 (0)