diff --git a/Lib/http/client.py b/Lib/http/client.py index 73c3256734a64f..dc0b64f4f06f98 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 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") if len(line) > _MAXLINE: @@ -911,6 +916,13 @@ 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): + 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): """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..40cea0685085d3 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), '') + + @unittest.skipUnless(hasattr(client, 'HTTPSConnection'), + 'ssl support required') + def test_https_connection_repr(self): + 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): + 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 = ( 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..91492495bbff14 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-08-00-00-00.gh-issue-145651.HtRepr.rst @@ -0,0 +1,3 @@ +Add :meth:`~object.__repr__` support to :class:`http.client.HTTPConnection` and +:class:`http.client.HTTPResponse`, showing host/port and status/reason +respectively.