Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Lib/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.

Expand Down
42 changes: 42 additions & 0 deletions Lib/test/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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), '<HTTPConnection example.com:80>')

def test_http_connection_repr_explicit_port(self):
conn = client.HTTPConnection('example.com', 8080)
self.assertEqual(repr(conn), '<HTTPConnection example.com:8080>')

@unittest.skipUnless(hasattr(client, 'HTTPSConnection'),
'ssl support required')
def test_https_connection_repr(self):
conn = client.HTTPSConnection('example.com')
self.assertEqual(repr(conn), '<HTTPSConnection example.com:443>')

@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), '<HTTPSConnection example.com:8443>')

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), '<HTTPResponse>')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this representation really make sense? it looks a bit... invalid. Maybe <HTTPResponse (notset)> or something like this ("notset" is not the best word though; what does requests and httpx do for such cases?)


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), '<HTTPResponse [200 OK]>')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the representation actually the same as for httpx/requests?


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), '<HTTPResponse [404 Not Found]>')


class TunnelTests(TestCase):
def setUp(self):
response_text = (
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading