From e4e2528e6f501e46391143ffc1b4e30675acc5d9 Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Sun, 8 Mar 2026 16:45:29 -0700 Subject: [PATCH 1/5] gh-145651: Add __repr__ to http.client.HTTPConnection and HTTPResponse --- Lib/http/client.py | 8 ++++++++ Lib/test/test_httplib.py | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/Lib/http/client.py b/Lib/http/client.py index 73c3256734a64f..3fc843cf09da7e 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -294,6 +294,11 @@ def __init__(self, sock, debuglevel=0, method=None, url=None): self.length = _UNKNOWN # number of bytes left in response self.will_close = _UNKNOWN # conn will close at end of response + def __repr__(self): + if self.status is _UNKNOWN: + return '<%s>' % (self.__class__.__name__,) + return '<%s [%s %s]>' % (self.__class__.__name__, self.status, self.reason) + def _read_status(self): line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") if len(line) > _MAXLINE: @@ -911,6 +916,9 @@ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, # tests to replace it with a suitable mockup self._create_connection = socket.create_connection + def __repr__(self): + return '<%s %s:%s>' % (self.__class__.__name__, self.host, self.port if self.port is not None else self.default_port) + def set_tunnel(self, host, port=None, headers=None): """Set up host and port for HTTP CONNECT tunnelling. diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 44044d0385c72e..dc875c17411562 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -2363,6 +2363,48 @@ def test_getting_header_defaultint(self): header = self.resp.getheader('No-Such-Header',default=42) self.assertEqual(header, 42) +class ReprTest(TestCase): + + def test_http_connection_repr_default_port(self): + conn = client.HTTPConnection('example.com') + self.assertEqual(repr(conn), '') + + def test_http_connection_repr_explicit_port(self): + conn = client.HTTPConnection('example.com', 8080) + self.assertEqual(repr(conn), '') + + def test_https_connection_repr(self): + if not hasattr(client, 'HTTPSConnection'): + self.skipTest('ssl support required') + conn = client.HTTPSConnection('example.com') + self.assertEqual(repr(conn), '') + + def test_https_connection_repr_explicit_port(self): + if not hasattr(client, 'HTTPSConnection'): + self.skipTest('ssl support required') + conn = client.HTTPSConnection('example.com', 8443) + self.assertEqual(repr(conn), '') + + def test_http_response_repr_before_read(self): + sock = FakeSocket(b'HTTP/1.1 200 OK\r\n\r\n') + resp = client.HTTPResponse(sock) + self.assertEqual(repr(resp), '') + + def test_http_response_repr_after_read(self): + body = b'HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n' + sock = FakeSocket(body) + resp = client.HTTPResponse(sock) + resp.begin() + self.assertEqual(repr(resp), '') + + def test_http_response_repr_not_found(self): + body = b'HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n' + sock = FakeSocket(body) + resp = client.HTTPResponse(sock) + resp.begin() + self.assertEqual(repr(resp), '') + + class TunnelTests(TestCase): def setUp(self): response_text = ( From a3fb5a4a2eaf2ab3a13c89fe0a80a299b4546cfe Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Sun, 8 Mar 2026 17:34:28 -0700 Subject: [PATCH 2/5] gh-145651: Add NEWS entry for http.client repr changes --- .../Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst diff --git a/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst new file mode 100644 index 00000000000000..23af6c195307ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst @@ -0,0 +1,3 @@ +Add :func:`repr` support to :class:`http.client.HTTPConnection` and +:class:`http.client.HTTPResponse`, showing host/port and status/reason +respectively. From 5628773185b8ed0bf682a8d6e57d77e823e3d31d Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Sun, 8 Mar 2026 18:02:54 -0700 Subject: [PATCH 3/5] Address review feedback from picnixz --- Lib/http/client.py | 7 ++++--- Lib/test/test_httplib.py | 8 ++++---- .../2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index 3fc843cf09da7e..f543e067ad531d 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -296,8 +296,8 @@ def __init__(self, sock, debuglevel=0, method=None, url=None): def __repr__(self): if self.status is _UNKNOWN: - return '<%s>' % (self.__class__.__name__,) - return '<%s [%s %s]>' % (self.__class__.__name__, self.status, self.reason) + return f'<{self.__class__.__name__}>' + return f'<{self.__class__.__name__} [{self.status} {self.reason}]>' def _read_status(self): line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") @@ -917,7 +917,8 @@ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, self._create_connection = socket.create_connection def __repr__(self): - return '<%s %s:%s>' % (self.__class__.__name__, self.host, self.port if self.port is not None else self.default_port) + port = self.port if self.port is not None else self.default_port + return f'<{self.__class__.__name__} {self.host}:{port}>' def set_tunnel(self, host, port=None, headers=None): """Set up host and port for HTTP CONNECT tunnelling. diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index dc875c17411562..40cea0685085d3 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -2373,15 +2373,15 @@ def test_http_connection_repr_explicit_port(self): conn = client.HTTPConnection('example.com', 8080) self.assertEqual(repr(conn), '') + @unittest.skipUnless(hasattr(client, 'HTTPSConnection'), + 'ssl support required') def test_https_connection_repr(self): - if not hasattr(client, 'HTTPSConnection'): - self.skipTest('ssl support required') conn = client.HTTPSConnection('example.com') self.assertEqual(repr(conn), '') + @unittest.skipUnless(hasattr(client, 'HTTPSConnection'), + 'ssl support required') def test_https_connection_repr_explicit_port(self): - if not hasattr(client, 'HTTPSConnection'): - self.skipTest('ssl support required') conn = client.HTTPSConnection('example.com', 8443) self.assertEqual(repr(conn), '') diff --git a/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst index 23af6c195307ff..91492495bbff14 100644 --- a/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst +++ b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst @@ -1,3 +1,3 @@ -Add :func:`repr` support to :class:`http.client.HTTPConnection` and +Add :meth:`~object.__repr__` support to :class:`http.client.HTTPConnection` and :class:`http.client.HTTPResponse`, showing host/port and status/reason respectively. From 7e113ba9c09bdfa5869b9abd9a807c5daa7a992c Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Sun, 8 Mar 2026 18:06:21 -0700 Subject: [PATCH 4/5] Use if block instead of ternary for port fallback --- Lib/http/client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index f543e067ad531d..a4e835b36367ba 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -917,7 +917,10 @@ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, self._create_connection = socket.create_connection def __repr__(self): - port = self.port if self.port is not None else self.default_port + if self.port is not None: + port = self.port + else: + port = self.default_port return f'<{self.__class__.__name__} {self.host}:{port}>' def set_tunnel(self, host, port=None, headers=None): From aeb2b4ce691162a43bd7d55029817a3929bd5bb3 Mon Sep 17 00:00:00 2001 From: Jack Danger Date: Sun, 8 Mar 2026 18:06:45 -0700 Subject: [PATCH 5/5] Check self.port is None first for readability --- Lib/http/client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index a4e835b36367ba..dc0b64f4f06f98 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -917,10 +917,10 @@ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, self._create_connection = socket.create_connection def __repr__(self): - if self.port is not None: - port = self.port - else: + if self.port is None: port = self.default_port + else: + port = self.port return f'<{self.__class__.__name__} {self.host}:{port}>' def set_tunnel(self, host, port=None, headers=None):