From daaea93917e9f7247ab991286858f8f94ca8d117 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:54:46 +0000 Subject: [PATCH] [PR #9873/c9698c8e backport][3.11] Make creating `RequestInfo` backwards compatible with 3.10 (#9874) Co-authored-by: J. Nick Koston fixes #9866 --- CHANGES/9873.bugfix.rst | 1 + aiohttp/client_reqrep.py | 25 +++++++++++++++++++-- tests/test_client_request.py | 43 ++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 CHANGES/9873.bugfix.rst diff --git a/CHANGES/9873.bugfix.rst b/CHANGES/9873.bugfix.rst new file mode 100644 index 00000000000..90f708fa879 --- /dev/null +++ b/CHANGES/9873.bugfix.rst @@ -0,0 +1 @@ +Added a backward compatibility layer to `~aiohttp.RequestInfo` to allow creating these objects without a `real_url` -- by :user:`bdraco`. diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index e33d3d4b1dd..267b509b0e6 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -42,6 +42,7 @@ from .compression_utils import HAS_BROTLI from .formdata import FormData from .helpers import ( + _SENTINEL, BaseTimerContext, BasicAuth, HeadersMixin, @@ -103,13 +104,31 @@ class ContentDisposition: filename: Optional[str] -class RequestInfo(NamedTuple): +class _RequestInfo(NamedTuple): url: URL method: str headers: "CIMultiDictProxy[str]" real_url: URL +class RequestInfo(_RequestInfo): + + def __new__( + cls, + url: URL, + method: str, + headers: "CIMultiDictProxy[str]", + real_url: URL = _SENTINEL, # type: ignore[assignment] + ) -> "RequestInfo": + """Create a new RequestInfo instance. + + For backwards compatibility, the real_url parameter is optional. + """ + return tuple.__new__( + cls, (url, method, headers, url if real_url is _SENTINEL else real_url) + ) + + class Fingerprint: HASHFUNC_BY_DIGESTLEN = { 16: md5, @@ -391,7 +410,9 @@ def port(self) -> Optional[int]: def request_info(self) -> RequestInfo: headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers) # These are created on every request, so we use a NamedTuple - # for performance reasons. + # for performance reasons. We don't use the RequestInfo.__new__ + # method because it has a different signature which is provided + # for backwards compatibility only. return tuple.__new__( RequestInfo, (self.url, self.method, headers, self.original_url) ) diff --git a/tests/test_client_request.py b/tests/test_client_request.py index 870c9666f34..20ccf6c03d1 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -1492,3 +1492,46 @@ async def test_connection_key_without_proxy() -> None: ) assert req.connection_key.proxy_headers_hash is None await req.close() + + +def test_request_info_back_compat() -> None: + """Test RequestInfo can be created without real_url.""" + url = URL("http://example.com") + other_url = URL("http://example.org") + assert ( + aiohttp.RequestInfo( + url=url, method="GET", headers=CIMultiDictProxy(CIMultiDict()) + ).real_url + is url + ) + assert ( + aiohttp.RequestInfo(url, "GET", CIMultiDictProxy(CIMultiDict())).real_url is url + ) + assert ( + aiohttp.RequestInfo( + url, "GET", CIMultiDictProxy(CIMultiDict()), real_url=url + ).real_url + is url + ) + assert ( + aiohttp.RequestInfo( + url, "GET", CIMultiDictProxy(CIMultiDict()), real_url=other_url + ).real_url + is other_url + ) + + +def test_request_info_tuple_new() -> None: + """Test RequestInfo must be created with real_url using tuple.__new__.""" + url = URL("http://example.com") + with pytest.raises(IndexError): + tuple.__new__( + aiohttp.RequestInfo, (url, "GET", CIMultiDictProxy(CIMultiDict())) + ).real_url + + assert ( + tuple.__new__( + aiohttp.RequestInfo, (url, "GET", CIMultiDictProxy(CIMultiDict()), url) + ).real_url + is url + )