Skip to content

Commit

Permalink
Reduce initialization overhead in ClientResponse (#10030)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdraco authored Nov 24, 2024
1 parent e79b2d5 commit cdc01cd
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES/10030.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improved performance of creating :class:`aiohttp.ClientResponse` objects -- by :user:`bdraco`.
28 changes: 15 additions & 13 deletions aiohttp/client_reqrep.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,16 +760,23 @@ class ClientResponse(HeadersMixin):
reason: Optional[str] = None # Reason-Phrase

content: StreamReader = None # type: ignore[assignment] # Payload stream
_body: Optional[bytes] = None
_headers: CIMultiDictProxy[str] = None # type: ignore[assignment]
_history: Tuple["ClientResponse", ...] = ()
_raw_headers: RawHeaders = None # type: ignore[assignment]

_connection: Optional["Connection"] = None # current connection
_continue: Optional["asyncio.Future[bool]"] = None
_source_traceback: Optional[traceback.StackSummary] = None
_session: Optional["ClientSession"] = None
# set up by ClientRequest after ClientResponse object creation
# post-init stage allows to not change ctor signature
_closed = True # to allow __del__ for non-initialized properly response
_released = False
_in_context = False

_resolve_charset: Callable[["ClientResponse", bytes], str] = lambda *_: "utf-8"

__writer: Optional["asyncio.Task[None]"] = None

def __init__(
Expand All @@ -785,35 +792,30 @@ def __init__(
loop: asyncio.AbstractEventLoop,
session: "ClientSession",
) -> None:
assert isinstance(url, URL)
# URL forbids subclasses, so a simple type check is enough.
assert type(url) is URL
super().__init__()

self.method = method
self.cookies = SimpleCookie()

self._real_url = url
self._url = url.with_fragment(None) if url.raw_fragment else url
self._body: Optional[bytes] = None
if writer is not None:
self._writer = writer
self._continue = continue100 # None by default
self._closed = True
self._history: Tuple[ClientResponse, ...] = ()
if continue100 is not None:
self._continue = continue100
self._request_info = request_info
self._timer = timer if timer is not None else TimerNoop()
self._cache: Dict[str, Any] = {}
self._traces = traces
self._loop = loop
# store a reference to session #1985
self._session: Optional[ClientSession] = session
# Save reference to _resolve_charset, so that get_encoding() will still
# work after the response has finished reading the body.
if session is None:
# TODO: Fix session=None in tests (see ClientRequest.__init__).
self._resolve_charset: Callable[["ClientResponse", bytes], str] = ( # type: ignore[unreachable]
lambda *_: "utf-8"
)
else:
# TODO: Fix session=None in tests (see ClientRequest.__init__).
if session is not None:
# store a reference to session #1985
self._session = session
self._resolve_charset = session._resolve_charset
if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1))
Expand Down
18 changes: 18 additions & 0 deletions tests/test_client_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,24 @@ def test_content_disposition_no_header() -> None:
assert response.content_disposition is None


def test_default_encoding_is_utf8() -> None:
response = ClientResponse(
"get",
URL("http://def-cl-resp.org"),
request_info=mock.Mock(),
writer=WriterMock(),
continue100=None,
timer=TimerNoop(),
traces=[],
loop=mock.Mock(),
session=None, # type: ignore[arg-type]
)
response._headers = CIMultiDictProxy(CIMultiDict({}))
response._body = b""

assert response.get_encoding() == "utf-8"


def test_response_request_info() -> None:
url = URL("http://def-cl-resp.org")
h = {"Content-Type": "application/json;charset=cp1251"}
Expand Down

0 comments on commit cdc01cd

Please sign in to comment.