Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ruff linter: Use the default line-length #2922

Merged
merged 5 commits into from
Nov 29, 2023
Merged
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
8 changes: 6 additions & 2 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1264,7 +1264,9 @@ def __enter__(self: T) -> T:
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)

Expand Down Expand Up @@ -1980,7 +1982,9 @@ async def __aenter__(self: U) -> U:
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)

Expand Down
3 changes: 2 additions & 1 deletion httpx/_decoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ class LineDecoder:
"""
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:
Expand Down
10 changes: 8 additions & 2 deletions httpx/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,10 @@ class ResponseNotRead(StreamError):
"""

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)


Expand All @@ -323,7 +326,10 @@ class RequestNotRead(StreamError):
"""

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)


Expand Down
11 changes: 6 additions & 5 deletions httpx/_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ def print_help() -> None:
)
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(
Expand All @@ -75,8 +76,8 @@ def print_help() -> None:

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.")
Expand Down
3 changes: 2 additions & 1 deletion httpx/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@ def __init__(
# 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:
Expand Down
17 changes: 10 additions & 7 deletions httpx/_multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def __init__(
)
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] = (
Expand Down Expand Up @@ -96,11 +97,13 @@ def __init__(self, name: str, value: FileTypes) -> None:
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
Expand All @@ -116,9 +119,9 @@ def __init__(self, name: str, value: FileTypes) -> None:

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):
Expand Down
6 changes: 4 additions & 2 deletions httpx/_transports/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ def __init__(
)
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.
Expand Down Expand Up @@ -328,7 +329,8 @@ def __init__(
)
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.
Expand Down
41 changes: 22 additions & 19 deletions httpx/_urlparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,24 +360,25 @@ def normalize_port(

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 ':'"
Expand Down Expand Up @@ -449,16 +450,18 @@ def quote(string: str, safe: str = "/") -> str:


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])
22 changes: 14 additions & 8 deletions httpx/_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,23 @@ class URL:
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__(
Expand Down Expand Up @@ -115,7 +117,8 @@ 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
Expand Down Expand Up @@ -305,7 +308,8 @@ def raw(self) -> RawURL:
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,
Expand Down Expand Up @@ -342,7 +346,9 @@ def copy_with(self, **kwargs: typing.Any) -> "URL":

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)
Expand Down
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,6 @@ replacement = 'src="https://raw.githubusercontent.com/encode/httpx/master/\1"'
[tool.ruff]
select = ["E", "F", "I", "B", "PIE"]
ignore = ["B904", "B028"]
line-length = 88
tomchristie marked this conversation as resolved.
Show resolved Hide resolved

[tool.ruff.pycodestyle]
max-line-length = 120

[tool.ruff.isort]
combine-as-imports = true
Expand Down
3 changes: 2 additions & 1 deletion tests/client/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,8 @@ async def test_digest_auth_resets_nonce_count_after_401() -> None:
# 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
Expand Down
6 changes: 3 additions & 3 deletions tests/models/test_cookies.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def test_cookies_repr():
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 />]>"
)
5 changes: 4 additions & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ def test_create_ssl_context_with_get_request(server, cert_pem_file):

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


Expand Down
5 changes: 4 additions & 1 deletion tests/test_multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ def test_multipart_file_tuple_headers(file_content_type: typing.Optional[str]) -


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"
Expand Down
3 changes: 2 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ def test_logging_redirect_chain(server, caplog):
(
"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",
Expand Down