if self._state != ClientState.UNOPENED:
msg = {
ClientState.OPENED: "Cannot open a client instance more than once.",
- ClientState.CLOSED: "Cannot reopen a client instance, once it has been closed.",
+ ClientState.CLOSED: (
+ "Cannot reopen a client instance, once it has been closed."
+ ),
}[self._state]
raise RuntimeError(msg)
if self._state != ClientState.UNOPENED:
msg = {
ClientState.OPENED: "Cannot open a client instance more than once.",
- ClientState.CLOSED: "Cannot reopen a client instance, once it has been closed.",
+ ClientState.CLOSED: (
+ "Cannot reopen a client instance, once it has been closed."
+ ),
}[self._state]
raise RuntimeError(msg)
"""
Handles incrementally reading lines from text.
- Has the same behaviour as the stdllib splitlines, but handling the input iteratively.
+ Has the same behaviour as the stdllib splitlines,
+ but handling the input iteratively.
"""
def __init__(self) -> None:
"""
def __init__(self) -> None:
- message = "Attempted to access streaming response content, without having called `read()`."
+ message = (
+ "Attempted to access streaming response content,"
+ " without having called `read()`."
+ )
super().__init__(message)
"""
def __init__(self) -> None:
- message = "Attempted to access streaming request content, without having called `read()`."
+ message = (
+ "Attempted to access streaming request content,"
+ " without having called `read()`."
+ )
super().__init__(message)
)
table.add_row(
"--auth [cyan]<USER PASS>",
- "Username and password to include in the request. Specify '-' for the password to use "
- "a password prompt. Note that using --verbose/-v will expose the Authorization "
- "header, including the password encoding in a trivially reversible format.",
+ "Username and password to include in the request. Specify '-' for the password"
+ " to use a password prompt. Note that using --verbose/-v will expose"
+ " the Authorization header, including the password encoding"
+ " in a trivially reversible format.",
)
table.add_row(
table.add_row(
"--timeout [cyan]FLOAT",
- "Timeout value to use for network operations, such as establishing the connection, "
- "reading some data, etc... [Default: 5.0]",
+ "Timeout value to use for network operations, such as establishing the"
+ " connection, reading some data, etc... [Default: 5.0]",
)
table.add_row("--follow-redirects", "Automatically follow redirects.")
# Using `content=...` implies automatically populated `Host` and content
# headers, of either `Content-Length: ...` or `Transfer-Encoding: chunked`.
#
- # Using `stream=...` will not automatically include *any* auto-populated headers.
+ # Using `stream=...` will not automatically include *any*
+ # auto-populated headers.
#
# As an end-user you don't really need `stream=...`. It's only
# useful when:
)
if value is not None and not isinstance(value, (str, bytes, int, float)):
raise TypeError(
- f"Invalid type for value. Expected primitive type, got {type(value)}: {value!r}"
+ "Invalid type for value. Expected primitive type,"
+ f" got {type(value)}: {value!r}"
)
self.name = name
self.value: typing.Union[str, bytes] = (
content_type: typing.Optional[str] = None
# This large tuple based API largely mirror's requests' API
- # It would be good to think of better APIs for this that we could include in httpx 2.0
- # since variable length tuples (especially of 4 elements) are quite unwieldly
+ # It would be good to think of better APIs for this that we could
+ # include in httpx 2.0 since variable length tuples(especially of 4 elements)
+ # are quite unwieldly
if isinstance(value, tuple):
if len(value) == 2:
- # neither the 3rd parameter (content_type) nor the 4th (headers) was included
+ # neither the 3rd parameter (content_type) nor the 4th (headers)
+ # was included
filename, fileobj = value # type: ignore
elif len(value) == 3:
filename, fileobj, content_type = value # type: ignore
has_content_type_header = any("content-type" in key.lower() for key in headers)
if content_type is not None and not has_content_type_header:
- # note that unlike requests, we ignore the content_type
- # provided in the 3rd tuple element if it is also included in the headers
- # requests does the opposite (it overwrites the header with the 3rd tuple element)
+ # note that unlike requests, we ignore the content_type provided in the 3rd
+ # tuple element if it is also included in the headers requests does
+ # the opposite (it overwrites the headerwith the 3rd tuple element)
headers["Content-Type"] = content_type
if isinstance(fileobj, io.StringIO):
)
else: # pragma: no cover
raise ValueError(
- f"Proxy protocol must be either 'http', 'https', or 'socks5', but got {proxy.url.scheme!r}."
+ "Proxy protocol must be either 'http', 'https', or 'socks5',"
+ f" but got {proxy.url.scheme!r}."
)
def __enter__(self: T) -> T: # Use generics for subclass support.
)
else: # pragma: no cover
raise ValueError(
- f"Proxy protocol must be either 'http', 'https', or 'socks5', but got {proxy.url.scheme!r}."
+ "Proxy protocol must be either 'http', 'https', or 'socks5',"
+ " but got {proxy.url.scheme!r}."
)
async def __aenter__(self: A) -> A: # Use generics for subclass support.
def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None:
"""
- Path validation rules that depend on if the URL contains a scheme or authority component.
+ Path validation rules that depend on if the URL contains
+ a scheme or authority component.
See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3
"""
if has_authority:
- # > If a URI contains an authority component, then the path component
- # > must either be empty or begin with a slash ("/") character."
+ # If a URI contains an authority component, then the path component
+ # must either be empty or begin with a slash ("/") character."
if path and not path.startswith("/"):
raise InvalidURL("For absolute URLs, path must be empty or begin with '/'")
else:
- # > If a URI does not contain an authority component, then the path cannot begin
- # > with two slash characters ("//").
+ # If a URI does not contain an authority component, then the path cannot begin
+ # with two slash characters ("//").
if path.startswith("//"):
raise InvalidURL(
"URLs with no authority component cannot have a path starting with '//'"
)
- # > In addition, a URI reference (Section 4.1) may be a relative-path reference, in which
- # > case the first path segment cannot contain a colon (":") character.
+ # In addition, a URI reference (Section 4.1) may be a relative-path reference,
+ # in which case the first path segment cannot contain a colon (":") character.
if path.startswith(":") and not has_scheme:
raise InvalidURL(
"URLs with no scheme component cannot have a path starting with ':'"
def urlencode(items: typing.List[typing.Tuple[str, str]]) -> str:
- # We can use a much simpler version of the stdlib urlencode here because
- # we don't need to handle a bunch of different typing cases, such as bytes vs str.
- #
- # https://github.com/python/cpython/blob/b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb/Lib/urllib/parse.py#L926
- #
- # Note that we use '%20' encoding for spaces. and '%2F for '/'.
- # This is slightly different than `requests`, but is the behaviour that browsers use.
- #
- # See
- # - https://github.com/encode/httpx/issues/2536
- # - https://github.com/encode/httpx/issues/2721
- # - https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode
+ """
+ We can use a much simpler version of the stdlib urlencode here because
+ we don't need to handle a bunch of different typing cases, such as bytes vs str.
+
+ https://github.com/python/cpython/blob/b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb/Lib/urllib/parse.py#L926
+
+ Note that we use '%20' encoding for spaces. and '%2F for '/'.
+ This is slightly different than `requests`, but is the behaviour that browsers use.
+
+ See
+ - https://github.com/encode/httpx/issues/2536
+ - https://github.com/encode/httpx/issues/2721
+ - https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode
+ """
return "&".join([quote(k, safe="") + "=" + quote(v, safe="") for k, v in items])
assert url.raw_host == b"xn--fiqs8s.icom.museum"
* `url.port` is either None or an integer. URLs that include the default port for
- "http", "https", "ws", "wss", and "ftp" schemes have their port normalized to `None`.
+ "http", "https", "ws", "wss", and "ftp" schemes have their port
+ normalized to `None`.
assert httpx.URL("http://example.com") == httpx.URL("http://example.com:80")
assert httpx.URL("http://example.com").port is None
assert httpx.URL("http://example.com:80").port is None
- * `url.userinfo` is raw bytes, without URL escaping. Usually you'll want to work with
- `url.username` and `url.password` instead, which handle the URL escaping.
+ * `url.userinfo` is raw bytes, without URL escaping. Usually you'll want to work
+ with `url.username` and `url.password` instead, which handle the URL escaping.
* `url.raw_path` is raw bytes of both the path and query, without URL escaping.
This portion is used as the target when constructing HTTP requests. Usually you'll
want to work with `url.path` instead.
- * `url.query` is raw bytes, without URL escaping. A URL query string portion can only
- be properly URL escaped when decoding the parameter names and values themselves.
+ * `url.query` is raw bytes, without URL escaping. A URL query string portion can
+ only be properly URL escaped when decoding the parameter names and values
+ themselves.
"""
def __init__(
self._uri_reference = url._uri_reference.copy_with(**kwargs)
else:
raise TypeError(
- f"Invalid type for url. Expected str or httpx.URL, got {type(url)}: {url!r}"
+ "Invalid type for url. Expected str or httpx.URL,"
+ f" got {type(url)}: {url!r}"
)
@property
Provides the (scheme, host, port, target) for the outgoing request.
In older versions of `httpx` this was used in the low-level transport API.
- We no longer use `RawURL`, and this property will be deprecated in a future release.
+ We no longer use `RawURL`, and this property will be deprecated
+ in a future release.
"""
return RawURL(
self.raw_scheme,
For example:
- url = httpx.URL("https://www.example.com").copy_with(username="jo@gmail.com", password="a secret")
+ url = httpx.URL("https://www.example.com").copy_with(
+ username="jo@gmail.com", password="a secret"
+ )
assert url == "https://jo%40email.com:a%20secret@www.example.com"
"""
return URL(self, **kwargs)
[tool.ruff]
select = ["E", "F", "I", "B", "PIE"]
ignore = ["B904", "B028"]
-line-length = 88
-
-[tool.ruff.pycodestyle]
-max-line-length = 120
[tool.ruff.isort]
combine-as-imports = true
# with this we now force a 401 on a subsequent (but initial) request
app.send_response_after_attempt = 2
- # we expect the client again to try to authenticate, i.e. the history length must be 1
+ # we expect the client again to try to authenticate,
+ # i.e. the history length must be 1
response_2 = await client.get(url, auth=auth)
assert response_2.status_code == 200
assert len(response_2.history) == 1
cookies.set(name="foo", value="bar", domain="http://blah.com")
cookies.set(name="fizz", value="buzz", domain="http://hello.com")
- assert (
- repr(cookies)
- == "<Cookies[<Cookie foo=bar for http://blah.com />, <Cookie fizz=buzz for http://hello.com />]>"
+ assert repr(cookies) == (
+ "<Cookies[<Cookie foo=bar for http://blah.com />,"
+ " <Cookie fizz=buzz for http://hello.com />]>"
)
def test_limits_repr():
limits = httpx.Limits(max_connections=100)
- expected = "Limits(max_connections=100, max_keepalive_connections=None, keepalive_expiry=5.0)"
+ expected = (
+ "Limits(max_connections=100, max_keepalive_connections=None,"
+ " keepalive_expiry=5.0)"
+ )
assert repr(limits) == expected
def test_multipart_headers_include_content_type() -> None:
- """Content-Type from 4th tuple parameter (headers) should override the 3rd parameter (content_type)"""
+ """
+ Content-Type from 4th tuple parameter (headers) should
+ override the 3rd parameter (content_type)
+ """
file_name = "test.txt"
file_content = io.BytesIO(b"<file content>")
file_content_type = "text/plain"
(
"httpx",
logging.INFO,
- 'HTTP Request: GET http://127.0.0.1:8000/redirect_301 "HTTP/1.1 301 Moved Permanently"',
+ "HTTP Request: GET http://127.0.0.1:8000/redirect_301"
+ ' "HTTP/1.1 301 Moved Permanently"',
),
(
"httpx",