diff --git a/httpx/_auth.py b/httpx/_auth.py index 27dc7f743b..b8d52b2118 100644 --- a/httpx/_auth.py +++ b/httpx/_auth.py @@ -55,9 +55,7 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non """ yield request - def sync_auth_flow( - self, request: Request - ) -> typing.Generator[Request, Response, None]: + def sync_auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: """ Execute the authentication flow synchronously. @@ -80,9 +78,7 @@ def sync_auth_flow( except StopIteration: break - async def async_auth_flow( - self, request: Request - ) -> typing.AsyncGenerator[Request, Response]: + async def async_auth_flow(self, request: Request) -> typing.AsyncGenerator[Request, Response]: """ Execute the authentication flow asynchronously. @@ -125,18 +121,14 @@ class BasicAuth(Auth): and uses HTTP Basic authentication. """ - def __init__( - self, username: typing.Union[str, bytes], password: typing.Union[str, bytes] - ): + def __init__(self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]): self._auth_header = self._build_auth_header(username, password) def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: request.headers["Authorization"] = self._auth_header yield request - def _build_auth_header( - self, username: typing.Union[str, bytes], password: typing.Union[str, bytes] - ) -> str: + def _build_auth_header(self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]) -> str: userpass = b":".join((to_bytes(username), to_bytes(password))) token = b64encode(userpass).decode() return f"Basic {token}" @@ -157,14 +149,10 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non yield request else: # Build a basic auth header with credentials from the netrc file. - request.headers["Authorization"] = self._build_auth_header( - username=auth_info[0], password=auth_info[2] - ) + request.headers["Authorization"] = self._build_auth_header(username=auth_info[0], password=auth_info[2]) yield request - def _build_auth_header( - self, username: typing.Union[str, bytes], password: typing.Union[str, bytes] - ) -> str: + def _build_auth_header(self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]) -> str: userpass = b":".join((to_bytes(username), to_bytes(password))) token = b64encode(userpass).decode() return f"Basic {token}" @@ -182,9 +170,7 @@ class DigestAuth(Auth): "SHA-512-SESS": hashlib.sha512, } - def __init__( - self, username: typing.Union[str, bytes], password: typing.Union[str, bytes] - ) -> None: + def __init__(self, username: typing.Union[str, bytes], password: typing.Union[str, bytes]) -> None: self._username = to_bytes(username) self._password = to_bytes(password) self._last_challenge: typing.Optional[_DigestAuthChallenge] = None @@ -192,9 +178,7 @@ def __init__( def auth_flow(self, request: Request) -> typing.Generator[Request, Response, None]: if self._last_challenge: - request.headers["Authorization"] = self._build_auth_header( - request, self._last_challenge - ) + request.headers["Authorization"] = self._build_auth_header(request, self._last_challenge) response = yield request @@ -214,16 +198,12 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non self._last_challenge = self._parse_challenge(request, response, auth_header) self._nonce_count = 1 - request.headers["Authorization"] = self._build_auth_header( - request, self._last_challenge - ) + request.headers["Authorization"] = self._build_auth_header(request, self._last_challenge) if response.cookies: Cookies(response.cookies).set_cookie_header(request=request) yield request - def _parse_challenge( - self, request: Request, response: Response, auth_header: str - ) -> "_DigestAuthChallenge": + def _parse_challenge(self, request: Request, response: Response, auth_header: str) -> "_DigestAuthChallenge": """ Returns a challenge from a Digest WWW-Authenticate header. These take the form of: @@ -245,16 +225,12 @@ def _parse_challenge( algorithm = header_dict.get("algorithm", "MD5") opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None qop = header_dict["qop"].encode() if "qop" in header_dict else None - return _DigestAuthChallenge( - realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop - ) + return _DigestAuthChallenge(realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop) except KeyError as exc: message = "Malformed Digest WWW-Authenticate header" raise ProtocolError(message, request=request) from exc - def _build_auth_header( - self, request: Request, challenge: "_DigestAuthChallenge" - ) -> str: + def _build_auth_header(self, request: Request, challenge: "_DigestAuthChallenge") -> str: hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm.upper()] def digest(data: bytes) -> bytes: @@ -316,18 +292,12 @@ def _get_header_value(self, header_fields: typing.Dict[str, bytes]) -> str: for i, (field, value) in enumerate(header_fields.items()): if i > 0: header_value += ", " - template = ( - QUOTED_TEMPLATE - if field not in NON_QUOTED_FIELDS - else NON_QUOTED_TEMPLATE - ) + template = QUOTED_TEMPLATE if field not in NON_QUOTED_FIELDS else NON_QUOTED_TEMPLATE header_value += template.format(field, to_str(value)) return header_value - def _resolve_qop( - self, qop: typing.Optional[bytes], request: Request - ) -> typing.Optional[bytes]: + def _resolve_qop(self, qop: typing.Optional[bytes], request: Request) -> typing.Optional[bytes]: if qop is None: return None qops = re.split(b", ?", qop) diff --git a/httpx/_client.py b/httpx/_client.py index cb475e0204..f3bbf7151d 100644 --- a/httpx/_client.py +++ b/httpx/_client.py @@ -87,9 +87,7 @@ class UseClientDefault: logger = logging.getLogger("httpx") USER_AGENT = f"python-httpx/{__version__}" -ACCEPT_ENCODING = ", ".join( - [key for key in SUPPORTED_DECODERS.keys() if key != "identity"] -) +ACCEPT_ENCODING = ", ".join([key for key in SUPPORTED_DECODERS.keys() if key != "identity"]) class ClientState(enum.Enum): @@ -112,9 +110,7 @@ class BoundSyncStream(SyncByteStream): ensures the `response.elapsed` is set once the response is closed. """ - def __init__( - self, stream: SyncByteStream, response: Response, timer: Timer - ) -> None: + def __init__(self, stream: SyncByteStream, response: Response, timer: Timer) -> None: self._stream = stream self._response = response self._timer = timer @@ -135,9 +131,7 @@ class BoundAsyncStream(AsyncByteStream): ensures the `response.elapsed` is set once the response is closed. """ - def __init__( - self, stream: AsyncByteStream, response: Response, timer: Timer - ) -> None: + def __init__(self, stream: AsyncByteStream, response: Response, timer: Timer) -> None: self._stream = stream self._response = response self._timer = timer @@ -166,9 +160,7 @@ def __init__( timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, follow_redirects: bool = False, max_redirects: int = DEFAULT_MAX_REDIRECTS, - event_hooks: typing.Optional[ - typing.Mapping[str, typing.List[EventHook]] - ] = None, + event_hooks: typing.Optional[typing.Mapping[str, typing.List[EventHook]]] = None, base_url: URLTypes = "", trust_env: bool = True, default_encoding: typing.Union[str, typing.Callable[[bytes], str]] = "utf-8", @@ -213,10 +205,7 @@ def _get_proxy_map( ) -> typing.Dict[str, typing.Optional[Proxy]]: if proxies is None: if allow_env_proxies: - return { - key: None if url is None else Proxy(url=url) - for key, url in get_environment_proxies().items() - } + return {key: None if url is None else Proxy(url=url) for key, url in get_environment_proxies().items()} return {} if isinstance(proxies, dict): new_proxies = {} @@ -241,9 +230,7 @@ def event_hooks(self) -> typing.Dict[str, typing.List[EventHook]]: return self._event_hooks @event_hooks.setter - def event_hooks( - self, event_hooks: typing.Dict[str, typing.List[EventHook]] - ) -> None: + def event_hooks(self, event_hooks: typing.Dict[str, typing.List[EventHook]]) -> None: self._event_hooks = { "request": list(event_hooks.get("request", [])), "response": list(event_hooks.get("response", [])), @@ -349,11 +336,7 @@ def build_request( params = self._merge_queryparams(params) extensions = {} if extensions is None else extensions if "timeout" not in extensions: - timeout = ( - self.timeout - if isinstance(timeout, UseClientDefault) - else Timeout(timeout) - ) + timeout = self.timeout if isinstance(timeout, UseClientDefault) else Timeout(timeout) extensions = dict(**extensions, timeout=timeout.as_dict()) return Request( method, @@ -390,9 +373,7 @@ def _merge_url(self, url: URLTypes) -> URL: return self.base_url.copy_with(raw_path=merge_raw_path) return merge_url - def _merge_cookies( - self, cookies: typing.Optional[CookieTypes] = None - ) -> typing.Optional[CookieTypes]: + def _merge_cookies(self, cookies: typing.Optional[CookieTypes] = None) -> typing.Optional[CookieTypes]: """ Merge a cookies argument together with any cookies on the client, to create the cookies used for the outgoing request. @@ -403,9 +384,7 @@ def _merge_cookies( return merged_cookies return cookies - def _merge_headers( - self, headers: typing.Optional[HeaderTypes] = None - ) -> typing.Optional[HeaderTypes]: + def _merge_headers(self, headers: typing.Optional[HeaderTypes] = None) -> typing.Optional[HeaderTypes]: """ Merge a headers argument together with any headers on the client, to create the headers used for the outgoing request. @@ -414,9 +393,7 @@ def _merge_headers( merged_headers.update(headers) return merged_headers - def _merge_queryparams( - self, params: typing.Optional[QueryParamTypes] = None - ) -> typing.Optional[QueryParamTypes]: + def _merge_queryparams(self, params: typing.Optional[QueryParamTypes] = None) -> typing.Optional[QueryParamTypes]: """ Merge a queryparams argument together with any queryparams on the client, to create the queryparams used for the outgoing request. @@ -443,9 +420,7 @@ def _build_request_auth( request: Request, auth: typing.Union[AuthTypes, UseClientDefault, None] = USE_CLIENT_DEFAULT, ) -> Auth: - auth = ( - self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth) - ) + auth = self._auth if isinstance(auth, UseClientDefault) else self._build_auth(auth) if auth is not None: return auth @@ -507,9 +482,7 @@ def _redirect_url(self, request: Request, response: Response) -> URL: try: url = URL(location) except InvalidURL as exc: - raise RemoteProtocolError( - f"Invalid URL in location header: {exc}.", request=request - ) from None + raise RemoteProtocolError(f"Invalid URL in location header: {exc}.", request=request) from None # Handle malformed 'Location' headers that are "absolute" form, have no host. # See: https://github.com/encode/httpx/issues/771 @@ -634,9 +607,7 @@ def __init__( follow_redirects: bool = False, limits: Limits = DEFAULT_LIMITS, max_redirects: int = DEFAULT_MAX_REDIRECTS, - event_hooks: typing.Optional[ - typing.Mapping[str, typing.List[EventHook]] - ] = None, + event_hooks: typing.Optional[typing.Mapping[str, typing.List[EventHook]]] = None, base_url: URLTypes = "", transport: typing.Optional[BaseTransport] = None, app: typing.Optional[typing.Callable[..., typing.Any]] = None, @@ -694,9 +665,7 @@ def __init__( for key, proxy in proxy_map.items() } if mounts is not None: - self._mounts.update( - {URLPattern(key): transport for key, transport in mounts.items()} - ) + self._mounts.update({URLPattern(key): transport for key, transport in mounts.items()}) self._mounts = dict(sorted(self._mounts.items())) @@ -890,11 +859,7 @@ def send( raise RuntimeError("Cannot send a request, as the client has been closed.") self._state = ClientState.OPENED - follow_redirects = ( - self.follow_redirects - if isinstance(follow_redirects, UseClientDefault) - else follow_redirects - ) + follow_redirects = self.follow_redirects if isinstance(follow_redirects, UseClientDefault) else follow_redirects auth = self._build_request_auth(request, auth) @@ -956,9 +921,7 @@ def _send_handling_redirects( ) -> Response: while True: if len(history) > self.max_redirects: - raise TooManyRedirects( - "Exceeded maximum allowed redirects.", request=request - ) + raise TooManyRedirects("Exceeded maximum allowed redirects.", request=request) for hook in self._event_hooks["request"]: hook(request) @@ -994,9 +957,7 @@ def _send_single_request(self, request: Request) -> Response: timer.sync_start() if not isinstance(request.stream, SyncByteStream): - raise RuntimeError( - "Attempted to send an async request with a sync Client instance." - ) + raise RuntimeError("Attempted to send an async request with a sync Client instance.") with request_context(request=request): response = transport.handle_request(request) @@ -1004,9 +965,7 @@ def _send_single_request(self, request: Request) -> Response: assert isinstance(response.stream, SyncByteStream) response.request = request - response.stream = BoundSyncStream( - response.stream, response=response, timer=timer - ) + response.stream = BoundSyncStream(response.stream, response=response, timer=timer) self.cookies.extract_cookies(response) response.default_encoding = self._default_encoding @@ -1359,9 +1318,7 @@ def __init__( follow_redirects: bool = False, limits: Limits = DEFAULT_LIMITS, max_redirects: int = DEFAULT_MAX_REDIRECTS, - event_hooks: typing.Optional[ - typing.Mapping[str, typing.List[typing.Callable[..., typing.Any]]] - ] = None, + event_hooks: typing.Optional[typing.Mapping[str, typing.List[typing.Callable[..., typing.Any]]]] = None, base_url: URLTypes = "", transport: typing.Optional[AsyncBaseTransport] = None, app: typing.Optional[typing.Callable[..., typing.Any]] = None, @@ -1420,9 +1377,7 @@ def __init__( for key, proxy in proxy_map.items() } if mounts is not None: - self._mounts.update( - {URLPattern(key): transport for key, transport in mounts.items()} - ) + self._mounts.update({URLPattern(key): transport for key, transport in mounts.items()}) self._mounts = dict(sorted(self._mounts.items())) def _init_transport( @@ -1606,11 +1561,7 @@ async def send( raise RuntimeError("Cannot send a request, as the client has been closed.") self._state = ClientState.OPENED - follow_redirects = ( - self.follow_redirects - if isinstance(follow_redirects, UseClientDefault) - else follow_redirects - ) + follow_redirects = self.follow_redirects if isinstance(follow_redirects, UseClientDefault) else follow_redirects auth = self._build_request_auth(request, auth) @@ -1672,9 +1623,7 @@ async def _send_handling_redirects( ) -> Response: while True: if len(history) > self.max_redirects: - raise TooManyRedirects( - "Exceeded maximum allowed redirects.", request=request - ) + raise TooManyRedirects("Exceeded maximum allowed redirects.", request=request) for hook in self._event_hooks["request"]: await hook(request) @@ -1711,18 +1660,14 @@ async def _send_single_request(self, request: Request) -> Response: await timer.async_start() if not isinstance(request.stream, AsyncByteStream): - raise RuntimeError( - "Attempted to send an sync request with an AsyncClient instance." - ) + raise RuntimeError("Attempted to send an sync request with an AsyncClient instance.") with request_context(request=request): response = await transport.handle_async_request(request) assert isinstance(response.stream, AsyncByteStream) response.request = request - response.stream = BoundAsyncStream( - response.stream, response=response, timer=timer - ) + response.stream = BoundAsyncStream(response.stream, response=response, timer=timer) self.cookies.extract_cookies(response) response.default_encoding = self._default_encoding diff --git a/httpx/_config.py b/httpx/_config.py index 45ed29ed70..035978b7da 100644 --- a/httpx/_config.py +++ b/httpx/_config.py @@ -48,9 +48,7 @@ def create_ssl_context( trust_env: bool = True, http2: bool = False, ) -> ssl.SSLContext: - return SSLConfig( - cert=cert, verify=verify, trust_env=trust_env, http2=http2 - ).ssl_context + return SSLConfig(cert=cert, verify=verify, trust_env=trust_env, http2=http2).ssl_context class SSLConfig: @@ -117,8 +115,7 @@ def load_ssl_context_verify(self) -> ssl.SSLContext: ca_bundle_path = Path(self.verify) else: raise IOError( - "Could not find a suitable TLS CA certificate bundle, " - "invalid path: {}".format(self.verify) + "Could not find a suitable TLS CA certificate bundle, " "invalid path: {}".format(self.verify) ) context = self._create_default_ssl_context() @@ -241,8 +238,7 @@ def __init__( else: if isinstance(timeout, UnsetType): raise ValueError( - "httpx.Timeout must either include a default, or set all " - "four parameters explicitly." + "httpx.Timeout must either include a default, or set all " "four parameters explicitly." ) self.connect = timeout if isinstance(connect, UnsetType) else connect self.read = timeout if isinstance(read, UnsetType) else read @@ -270,10 +266,7 @@ def __repr__(self) -> str: class_name = self.__class__.__name__ if len({self.connect, self.read, self.write, self.pool}) == 1: return f"{class_name}(timeout={self.connect})" - return ( - f"{class_name}(connect={self.connect}, " - f"read={self.read}, write={self.write}, pool={self.pool})" - ) + return f"{class_name}(connect={self.connect}, " f"read={self.read}, write={self.write}, pool={self.pool})" class Limits: @@ -346,11 +339,7 @@ def __init__( @property def raw_auth(self) -> typing.Optional[typing.Tuple[bytes, bytes]]: # The proxy authentication as raw bytes. - return ( - None - if self.auth is None - else (self.auth[0].encode("utf-8"), self.auth[1].encode("utf-8")) - ) + return None if self.auth is None else (self.auth[0].encode("utf-8"), self.auth[1].encode("utf-8")) def __repr__(self) -> str: # The authentication is represented with the password component masked. diff --git a/httpx/_decoders.py b/httpx/_decoders.py index 500ce7ffc3..7161561fa8 100644 --- a/httpx/_decoders.py +++ b/httpx/_decoders.py @@ -178,10 +178,7 @@ def decode(self, content: bytes) -> typing.List[bytes]: self._buffer.write(content) if self._buffer.tell() >= self._chunk_size: value = self._buffer.getvalue() - chunks = [ - value[i : i + self._chunk_size] - for i in range(0, len(value), self._chunk_size) - ] + chunks = [value[i : i + self._chunk_size] for i in range(0, len(value), self._chunk_size)] if len(chunks[-1]) == self._chunk_size: self._buffer.seek(0) self._buffer.truncate() @@ -217,10 +214,7 @@ def decode(self, content: str) -> typing.List[str]: self._buffer.write(content) if self._buffer.tell() >= self._chunk_size: value = self._buffer.getvalue() - chunks = [ - value[i : i + self._chunk_size] - for i in range(0, len(value), self._chunk_size) - ] + chunks = [value[i : i + self._chunk_size] for i in range(0, len(value), self._chunk_size)] if len(chunks[-1]) == self._chunk_size: self._buffer.seek(0) self._buffer.truncate() diff --git a/httpx/_exceptions.py b/httpx/_exceptions.py index 24a4f8aba3..d58128d08b 100644 --- a/httpx/_exceptions.py +++ b/httpx/_exceptions.py @@ -75,9 +75,7 @@ class RequestError(HTTPError): Base class for all exceptions that may occur when issuing a `.request()`. """ - def __init__( - self, message: str, *, request: typing.Optional["Request"] = None - ) -> None: + def __init__(self, message: str, *, request: typing.Optional["Request"] = None) -> None: super().__init__(message) # At the point an exception is raised we won't typically have a request # instance to associate it with. @@ -230,9 +228,7 @@ class HTTPStatusError(HTTPError): May be raised when calling `response.raise_for_status()` """ - def __init__( - self, message: str, *, request: "Request", response: "Response" - ) -> None: + def __init__(self, message: str, *, request: "Request", response: "Response") -> None: super().__init__(message) self.request = request self.response = response @@ -301,9 +297,7 @@ class StreamClosed(StreamError): """ def __init__(self) -> None: - message = ( - "Attempted to read or stream content, but the stream has " "been closed." - ) + message = "Attempted to read or stream content, but the stream has " "been closed." super().__init__(message) diff --git a/httpx/_main.py b/httpx/_main.py index 7c12ce841d..2d70930c63 100644 --- a/httpx/_main.py +++ b/httpx/_main.py @@ -26,9 +26,7 @@ def print_help() -> None: console.print() console.print("A next generation HTTP client.", justify="center") console.print() - console.print( - "Usage: [bold]httpx[/bold] [cyan] [OPTIONS][/cyan] ", justify="left" - ) + console.print("Usage: [bold]httpx[/bold] [cyan] [OPTIONS][/cyan] ", justify="left") console.print() table = rich.table.Table.grid(padding=1, pad_edge=True) @@ -43,12 +41,8 @@ def print_help() -> None: "-p, --params [cyan] ...", "Query parameters to include in the request URL.", ) - table.add_row( - "-c, --content [cyan]TEXT", "Byte content to include in the request body." - ) - table.add_row( - "-d, --data [cyan] ...", "Form data to include in the request body." - ) + table.add_row("-c, --content [cyan]TEXT", "Byte content to include in the request body.") + table.add_row("-d, --data [cyan] ...", "Form data to include in the request body.") table.add_row( "-f, --files [cyan] ...", "Form files to include in the request body.", @@ -58,9 +52,7 @@ def print_help() -> None: "-h, --headers [cyan] ...", "Include additional HTTP headers in the request.", ) - table.add_row( - "--cookies [cyan] ...", "Cookies to include in the request." - ) + table.add_row("--cookies [cyan] ...", "Cookies to include in the request.") table.add_row( "--auth [cyan]", "Username and password to include in the request. Specify '-' for the password to use " @@ -81,9 +73,7 @@ def print_help() -> None: table.add_row("--follow-redirects", "Automatically follow redirects.") table.add_row("--no-verify", "Disable SSL verification.") - table.add_row( - "--http2", "Send the request using HTTP/2, if the remote server supports it." - ) + table.add_row("--http2", "Send the request using HTTP/2, if the remote server supports it.") table.add_row( "--download [cyan]FILE", @@ -100,9 +90,7 @@ def get_lexer_for_response(response: Response) -> str: if content_type is not None: mime_type, _, _ = content_type.partition(";") try: - return typing.cast( - str, pygments.lexers.get_lexer_for_mimetype(mime_type.strip()).name - ) + return typing.cast(str, pygments.lexers.get_lexer_for_mimetype(mime_type.strip()).name) except pygments.util.ClassNotFound: # pragma: no cover pass return "" # pragma: no cover @@ -110,9 +98,7 @@ def get_lexer_for_response(response: Response) -> str: def format_request_headers(request: httpcore.Request, http2: bool = False) -> str: version = "HTTP/2" if http2 else "HTTP/1.1" - headers = [ - (name.lower() if http2 else name, value) for name, value in request.headers - ] + headers = [(name.lower() if http2 else name, value) for name, value in request.headers] method = request.method.decode("ascii") target = request.url.target.decode("ascii") lines = [f"{method} {target} {version}"] + [ @@ -128,11 +114,7 @@ def format_response_headers( headers: typing.List[typing.Tuple[bytes, bytes]], ) -> str: version = http_version.decode("ascii") - reason = ( - codes.get_reason_phrase(status) - if reason_phrase is None - else reason_phrase.decode("ascii") - ) + reason = codes.get_reason_phrase(status) if reason_phrase is None else reason_phrase.decode("ascii") lines = [f"{version} {status} {reason}"] + [ f"{name.decode('ascii')}: {value.decode('ascii')}" for name, value in headers ] @@ -204,9 +186,7 @@ def format_certificate(cert: _PeerCertRetDictType) -> str: # pragma: no cover return "\n".join(lines) -def trace( - name: str, info: typing.Mapping[str, typing.Any], verbose: bool = False -) -> None: +def trace(name: str, info: typing.Mapping[str, typing.Any], verbose: bool = False) -> None: console = rich.console.Console() if name == "connection.connect_tcp.started" and verbose: host = info["host"] diff --git a/httpx/_models.py b/httpx/_models.py index 8a6bda04bb..46d51eed09 100644 --- a/httpx/_models.py +++ b/httpx/_models.py @@ -159,10 +159,7 @@ def multi_items(self) -> typing.List[typing.Tuple[str, str]]: occurrences of the same key without concatenating into a single comma separated value. """ - return [ - (key.decode(self.encoding), value.decode(self.encoding)) - for _, key, value in self._list - ] + return [(key.decode(self.encoding), value.decode(self.encoding)) for _, key, value in self._list] def get(self, key: str, default: typing.Any = None) -> typing.Any: """ @@ -235,11 +232,7 @@ def __setitem__(self, key: str, value: str) -> None: set_value = value.encode(self._encoding or "utf-8") lookup_key = set_key.lower() - found_indexes = [ - idx - for idx, (_, item_key, _) in enumerate(self._list) - if item_key == lookup_key - ] + found_indexes = [idx for idx, (_, item_key, _) in enumerate(self._list) if item_key == lookup_key] for idx in reversed(found_indexes[1:]): del self._list[idx] @@ -256,11 +249,7 @@ def __delitem__(self, key: str) -> None: """ del_key = key.lower().encode(self.encoding) - pop_indexes = [ - idx - for idx, (_, item_key, _) in enumerate(self._list) - if item_key.lower() == del_key - ] + pop_indexes = [idx for idx, (_, item_key, _) in enumerate(self._list) if item_key.lower() == del_key] if not pop_indexes: raise KeyError(key) @@ -320,11 +309,7 @@ def __init__( stream: typing.Union[SyncByteStream, AsyncByteStream, None] = None, extensions: typing.Optional[RequestExtensions] = None, ): - self.method = ( - method.decode("ascii").upper() - if isinstance(method, bytes) - else method.upper() - ) + self.method = method.decode("ascii").upper() if isinstance(method, bytes) else method.upper() self.url = URL(url) if params is not None: self.url = self.url.copy_merge_params(params=params) @@ -342,9 +327,7 @@ def __init__( files=files, json=json, boundary=get_multipart_boundary_from_content_type( - content_type=content_type.encode(self.headers.encoding) - if content_type - else None + content_type=content_type.encode(self.headers.encoding) if content_type else None ), ) self._prepare(headers) @@ -378,9 +361,7 @@ def _prepare(self, default_headers: typing.Dict[str, str]) -> None: auto_headers: typing.List[typing.Tuple[bytes, bytes]] = [] has_host = "Host" in self.headers - has_content_length = ( - "Content-Length" in self.headers or "Transfer-Encoding" in self.headers - ) + has_content_length = "Content-Length" in self.headers or "Transfer-Encoding" in self.headers if not has_host and self.url.host: auto_headers.append((b"Host", self.url.netloc)) @@ -429,11 +410,7 @@ def __repr__(self) -> str: return f"<{class_name}({self.method!r}, {url!r})>" def __getstate__(self) -> typing.Dict[str, typing.Any]: - return { - name: value - for name, value in self.__dict__.items() - if name not in ["extensions", "stream"] - } + return {name: value for name, value in self.__dict__.items() if name not in ["extensions", "stream"]} def __setstate__(self, state: typing.Dict[str, typing.Any]) -> None: for name, value in state.items(): @@ -512,10 +489,7 @@ def elapsed(self) -> datetime.timedelta: cycle to complete. """ if not hasattr(self, "_elapsed"): - raise RuntimeError( - "'.elapsed' may only be accessed after the response " - "has been read or closed." - ) + raise RuntimeError("'.elapsed' may only be accessed after the response " "has been read or closed.") return self._elapsed @elapsed.setter @@ -528,9 +502,7 @@ def request(self) -> Request: Returns the request instance associated to the current response. """ if self._request is None: - raise RuntimeError( - "The request instance has not been set on this response." - ) + raise RuntimeError("The request instance has not been set on this response.") return self._request @request.setter @@ -610,9 +582,7 @@ def encoding(self, value: str) -> None: encoding will throw a ValueError. """ if hasattr(self, "_text"): - raise ValueError( - "Setting encoding after `text` has been accessed is not allowed." - ) + raise ValueError("Setting encoding after `text` has been accessed is not allowed.") self._encoding = value @property @@ -728,8 +698,7 @@ def raise_for_status(self) -> "Response": request = self._request if request is None: raise RuntimeError( - "Cannot call `raise_for_status` as the request " - "instance has not been set on this response." + "Cannot call `raise_for_status` as the request " "instance has not been set on this response." ) if self.is_success: @@ -815,9 +784,7 @@ def read(self) -> bytes: self._content = b"".join(self.iter_bytes()) return self._content - def iter_bytes( - self, chunk_size: typing.Optional[int] = None - ) -> typing.Iterator[bytes]: + def iter_bytes(self, chunk_size: typing.Optional[int] = None) -> typing.Iterator[bytes]: """ A byte-iterator over the decoded response content. This allows us to handle gzip, deflate, and brotli encoded responses. @@ -840,9 +807,7 @@ def iter_bytes( for chunk in chunker.flush(): yield chunk - def iter_text( - self, chunk_size: typing.Optional[int] = None - ) -> typing.Iterator[str]: + def iter_text(self, chunk_size: typing.Optional[int] = None) -> typing.Iterator[str]: """ A str-iterator over the decoded response content that handles both gzip, deflate, etc but also detects the content's @@ -870,9 +835,7 @@ def iter_lines(self) -> typing.Iterator[str]: for line in decoder.flush(): yield line - def iter_raw( - self, chunk_size: typing.Optional[int] = None - ) -> typing.Iterator[bytes]: + def iter_raw(self, chunk_size: typing.Optional[int] = None) -> typing.Iterator[bytes]: """ A byte-iterator over the raw response content. """ @@ -919,9 +882,7 @@ async def aread(self) -> bytes: self._content = b"".join([part async for part in self.aiter_bytes()]) return self._content - async def aiter_bytes( - self, chunk_size: typing.Optional[int] = None - ) -> typing.AsyncIterator[bytes]: + async def aiter_bytes(self, chunk_size: typing.Optional[int] = None) -> typing.AsyncIterator[bytes]: """ A byte-iterator over the decoded response content. This allows us to handle gzip, deflate, and brotli encoded responses. @@ -944,9 +905,7 @@ async def aiter_bytes( for chunk in chunker.flush(): yield chunk - async def aiter_text( - self, chunk_size: typing.Optional[int] = None - ) -> typing.AsyncIterator[str]: + async def aiter_text(self, chunk_size: typing.Optional[int] = None) -> typing.AsyncIterator[str]: """ A str-iterator over the decoded response content that handles both gzip, deflate, etc but also detects the content's @@ -974,9 +933,7 @@ async def aiter_lines(self) -> typing.AsyncIterator[str]: for line in decoder.flush(): yield line - async def aiter_raw( - self, chunk_size: typing.Optional[int] = None - ) -> typing.AsyncIterator[bytes]: + async def aiter_raw(self, chunk_size: typing.Optional[int] = None) -> typing.AsyncIterator[bytes]: """ A byte-iterator over the raw response content. """ @@ -1129,9 +1086,7 @@ def delete( for cookie in remove: self.jar.clear(cookie.domain, cookie.path, cookie.name) - def clear( - self, domain: typing.Optional[str] = None, path: typing.Optional[str] = None - ) -> None: + def clear(self, domain: typing.Optional[str] = None, path: typing.Optional[str] = None) -> None: """ Delete all cookies. Optionally include a domain and path in order to only delete a subset of all the cookies. @@ -1174,10 +1129,7 @@ def __bool__(self) -> bool: def __repr__(self) -> str: cookies_repr = ", ".join( - [ - f"" - for cookie in self.jar - ] + [f"" for cookie in self.jar] ) return f"" diff --git a/httpx/_multipart.py b/httpx/_multipart.py index 446f4ad2df..35974b9a59 100644 --- a/httpx/_multipart.py +++ b/httpx/_multipart.py @@ -40,28 +40,18 @@ class DataField: A single form field item, within a multipart form field. """ - def __init__( - self, name: str, value: typing.Union[str, bytes, int, float, None] - ) -> None: + def __init__(self, name: str, value: typing.Union[str, bytes, int, float, None]) -> None: if not isinstance(name, str): - raise TypeError( - f"Invalid type for name. Expected str, got {type(name)}: {name!r}" - ) + raise TypeError(f"Invalid type for name. Expected str, got {type(name)}: {name!r}") 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}" - ) + raise TypeError(f"Invalid type for value. Expected primitive type, got {type(value)}: {value!r}") self.name = name - self.value: typing.Union[str, bytes] = ( - value if isinstance(value, bytes) else primitive_value_to_str(value) - ) + self.value: typing.Union[str, bytes] = value if isinstance(value, bytes) else primitive_value_to_str(value) def render_headers(self) -> bytes: if not hasattr(self, "_headers"): name = format_form_param("name", self.name) - self._headers = b"".join( - [b"Content-Disposition: form-data; ", name, b"\r\n\r\n"] - ) + self._headers = b"".join([b"Content-Disposition: form-data; ", name, b"\r\n\r\n"]) return self._headers @@ -123,13 +113,9 @@ def __init__(self, name: str, value: FileTypes) -> None: headers["Content-Type"] = content_type if isinstance(fileobj, io.StringIO): - raise TypeError( - "Multipart file uploads require 'io.BytesIO', not 'io.StringIO'." - ) + raise TypeError("Multipart file uploads require 'io.BytesIO', not 'io.StringIO'.") if isinstance(fileobj, io.TextIOBase): - raise TypeError( - "Multipart file uploads must be opened in binary mode, not text mode." - ) + raise TypeError("Multipart file uploads must be opened in binary mode, not text mode.") self.filename = filename self.file = fileobj @@ -203,9 +189,7 @@ def __init__( boundary = binascii.hexlify(os.urandom(16)) self.boundary = boundary - self.content_type = "multipart/form-data; boundary=%s" % boundary.decode( - "ascii" - ) + self.content_type = "multipart/form-data; boundary=%s" % boundary.decode("ascii") self.fields = list(self._iter_fields(data, files)) def _iter_fields( diff --git a/httpx/_transports/asgi.py b/httpx/_transports/asgi.py index f67f0fbd5b..a5cee777ba 100644 --- a/httpx/_transports/asgi.py +++ b/httpx/_transports/asgi.py @@ -16,12 +16,8 @@ _Message = typing.Dict[str, typing.Any] _Receive = typing.Callable[[], typing.Awaitable[_Message]] -_Send = typing.Callable[ - [typing.Dict[str, typing.Any]], typing.Coroutine[None, None, None] -] -_ASGIApp = typing.Callable[ - [typing.Dict[str, typing.Any], _Receive, _Send], typing.Coroutine[None, None, None] -] +_Send = typing.Callable[[typing.Dict[str, typing.Any]], typing.Coroutine[None, None, None]] +_ASGIApp = typing.Callable[[typing.Dict[str, typing.Any], _Receive, _Send], typing.Coroutine[None, None, None]] def create_event() -> "Event": diff --git a/httpx/_transports/base.py b/httpx/_transports/base.py index f6fdfe6943..100a85675c 100644 --- a/httpx/_transports/base.py +++ b/httpx/_transports/base.py @@ -50,9 +50,7 @@ def handle_request(self, request: Request) -> Response: Returns a `Response` instance. """ - raise NotImplementedError( - "The 'handle_request' method must be implemented." - ) # pragma: no cover + raise NotImplementedError("The 'handle_request' method must be implemented.") # pragma: no cover def close(self) -> None: pass @@ -74,9 +72,7 @@ async def handle_async_request( self, request: Request, ) -> Response: - raise NotImplementedError( - "The 'handle_async_request' method must be implemented." - ) # pragma: no cover + raise NotImplementedError("The 'handle_async_request' method must be implemented.") # pragma: no cover async def aclose(self) -> None: pass diff --git a/httpx/_transports/default.py b/httpx/_transports/default.py index 7dba5b8208..76c543ce4e 100644 --- a/httpx/_transports/default.py +++ b/httpx/_transports/default.py @@ -64,7 +64,7 @@ def map_httpcore_exceptions() -> typing.Iterator[None]: try: yield - except Exception as exc: # noqa: PIE-786 + except Exception as exc: mapped_exc = None for from_exc, to_exc in HTTPCORE_EXC_MAP.items(): diff --git a/httpx/_transports/wsgi.py b/httpx/_transports/wsgi.py index a23d42c414..2d3e5260d2 100644 --- a/httpx/_transports/wsgi.py +++ b/httpx/_transports/wsgi.py @@ -136,9 +136,6 @@ def start_response( raise seen_exc_info[1] status_code = int(seen_status.split()[0]) - headers = [ - (key.encode("ascii"), value.encode("ascii")) - for key, value in seen_response_headers - ] + headers = [(key.encode("ascii"), value.encode("ascii")) for key, value in seen_response_headers] return Response(status_code, headers=headers, stream=stream) diff --git a/httpx/_types.py b/httpx/_types.py index 83cf35a32a..2a9fceb7fe 100644 --- a/httpx/_types.py +++ b/httpx/_types.py @@ -110,9 +110,7 @@ class SyncByteStream: def __iter__(self) -> Iterator[bytes]: - raise NotImplementedError( - "The '__iter__' method must be implemented." - ) # pragma: no cover + raise NotImplementedError("The '__iter__' method must be implemented.") # pragma: no cover yield b"" # pragma: no cover def close(self) -> None: @@ -124,9 +122,7 @@ def close(self) -> None: class AsyncByteStream: async def __aiter__(self) -> AsyncIterator[bytes]: - raise NotImplementedError( - "The '__aiter__' method must be implemented." - ) # pragma: no cover + raise NotImplementedError("The '__aiter__' method must be implemented.") # pragma: no cover yield b"" # pragma: no cover async def aclose(self) -> None: diff --git a/httpx/_urlparse.py b/httpx/_urlparse.py index 8e060424e8..2eab658e49 100644 --- a/httpx/_urlparse.py +++ b/httpx/_urlparse.py @@ -26,9 +26,7 @@ MAX_URL_LENGTH = 65536 # https://datatracker.ietf.org/doc/html/rfc3986.html#section-2.3 -UNRESERVED_CHARACTERS = ( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" -) +UNRESERVED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~" SUB_DELIMS = "!$&'()*+,;=" PERCENT_ENCODED_REGEX = re.compile("%[A-Fa-f0-9]{2}") @@ -59,9 +57,7 @@ # {host} # :{port} (optional) AUTHORITY_REGEX = re.compile( - ( - r"(?:(?P{userinfo})@)?" r"(?P{host})" r":?(?P{port})?" - ).format( + (r"(?:(?P{userinfo})@)?" r"(?P{host})" r":?(?P{port})?").format( userinfo="[^@]*", # Any character sequence not including '@'. host="(\\[.*\\]|[^:]*)", # Either any character sequence not including ':', # or an IPv6 address enclosed within square brackets. @@ -202,9 +198,7 @@ def urlparse(url: str = "", **kwargs: typing.Optional[str]) -> ParseResult: # If a component includes any ASCII control characters including \t, \r, \n, # then treat it as invalid. if any(char.isascii() and not char.isprintable() for char in value): - raise InvalidURL( - f"Invalid non-printable ASCII character in URL component '{key}'" - ) + raise InvalidURL(f"Invalid non-printable ASCII character in URL component '{key}'") # Ensure that keyword arguments match as a valid regex. if not COMPONENT_REGEX[key].fullmatch(value): @@ -246,9 +240,7 @@ def urlparse(url: str = "", **kwargs: typing.Optional[str]) -> ParseResult: parsed_port: typing.Optional[int] = normalize_port(port, scheme) has_scheme = parsed_scheme != "" - has_authority = ( - parsed_userinfo != "" or parsed_host != "" or parsed_port is not None - ) + has_authority = parsed_userinfo != "" or parsed_host != "" or parsed_port is not None validate_path(path, has_scheme=has_scheme, has_authority=has_authority) if has_authority: path = normalize_path(path) @@ -262,13 +254,9 @@ def urlparse(url: str = "", **kwargs: typing.Optional[str]) -> ParseResult: # For 'query' we need to drop '#' from the GEN_DELIMS set. # We also exclude '/' because it is more robust to replace it with a percent # encoding despite it not being a requirement of the spec. - parsed_query: typing.Optional[str] = ( - None if query is None else quote(query, safe=SUB_DELIMS + ":?[]@") - ) + parsed_query: typing.Optional[str] = None if query is None else quote(query, safe=SUB_DELIMS + ":?[]@") # For 'fragment' we can include all of the GEN_DELIMS set. - parsed_fragment: typing.Optional[str] = ( - None if fragment is None else quote(fragment, safe=SUB_DELIMS + ":/?#[]@") - ) + parsed_fragment: typing.Optional[str] = None if fragment is None else quote(fragment, safe=SUB_DELIMS + ":/?#[]@") # The parsed ASCII bytestrings are our canonical form. # All properties of the URL are derived from these. @@ -329,9 +317,7 @@ def encode_host(host: str) -> str: raise InvalidURL(f"Invalid IDNA hostname: {host!r}") -def normalize_port( - port: typing.Optional[typing.Union[str, int]], scheme: str -) -> typing.Optional[int]: +def normalize_port(port: typing.Optional[typing.Union[str, int]], scheme: str) -> typing.Optional[int]: # From https://tools.ietf.org/html/rfc3986#section-3.2.3 # # "A scheme may define a default port. For example, the "http" scheme @@ -350,9 +336,7 @@ def normalize_port( raise InvalidURL(f"Invalid port: {port!r}") # See https://url.spec.whatwg.org/#url-miscellaneous - default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get( - scheme - ) + default_port = {"ftp": 21, "http": 80, "https": 443, "ws": 80, "wss": 443}.get(scheme) if port_as_int == default_port: return None return port_as_int @@ -373,15 +357,11 @@ def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None: # > 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 '//'" - ) + 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. if path.startswith(":") and not has_scheme: - raise InvalidURL( - "URLs with no scheme component cannot have a path starting with ':'" - ) + raise InvalidURL("URLs with no scheme component cannot have a path starting with ':'") def normalize_path(path: str) -> str: @@ -443,9 +423,7 @@ def quote(string: str, safe: str = "/") -> str: return string NON_ESCAPED_CHARS = UNRESERVED_CHARACTERS + safe - return "".join( - [char if char in NON_ESCAPED_CHARS else percent_encode(char) for char in string] - ) + return "".join([char if char in NON_ESCAPED_CHARS else percent_encode(char) for char in string]) def urlencode(items: typing.List[typing.Tuple[str, str]]) -> str: diff --git a/httpx/_urls.py b/httpx/_urls.py index b023941b62..92c6822e68 100644 --- a/httpx/_urls.py +++ b/httpx/_urls.py @@ -68,9 +68,7 @@ class URL: be properly URL escaped when decoding the parameter names and values themselves. """ - def __init__( - self, url: typing.Union["URL", str] = "", **kwargs: typing.Any - ) -> None: + def __init__(self, url: typing.Union["URL", str] = "", **kwargs: typing.Any) -> None: if kwargs: allowed = { "scheme": str, @@ -114,9 +112,7 @@ def __init__( elif isinstance(url, URL): 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}" - ) + raise TypeError(f"Invalid type for url. Expected str or httpx.URL, got {type(url)}: {url!r}") @property def scheme(self) -> str: @@ -414,9 +410,7 @@ class QueryParams(typing.Mapping[str, str]): URL query parameters, as a multi-dict. """ - def __init__( - self, *args: typing.Optional[QueryParamTypes], **kwargs: typing.Any - ) -> None: + def __init__(self, *args: typing.Optional[QueryParamTypes], **kwargs: typing.Any) -> None: assert len(args) < 2, "Too many arguments." assert not (args and kwargs), "Cannot mix named and unnamed arguments." @@ -441,18 +435,12 @@ def __init__( # {"a": "123", "b": ["456", "789"]} # To dict inputs where values are always lists, like: # {"a": ["123"], "b": ["456", "789"]} - dict_value = { - k: list(v) if isinstance(v, (list, tuple)) else [v] - for k, v in value.items() - } + dict_value = {k: list(v) if isinstance(v, (list, tuple)) else [v] for k, v in value.items()} # Ensure that keys and values are neatly coerced to strings. # We coerce values `True` and `False` to JSON-like "true" and "false" # representations, and coerce `None` values to the empty string. - self._dict = { - str(k): [primitive_value_to_str(item) for item in v] - for k, v in dict_value.items() - } + self._dict = {str(k): [primitive_value_to_str(item) for item in v] for k, v in dict_value.items()} def keys(self) -> typing.KeysView[str]: """ @@ -631,12 +619,10 @@ def __repr__(self) -> str: def update(self, params: typing.Optional[QueryParamTypes] = None) -> None: raise RuntimeError( - "QueryParams are immutable since 0.18.0. " - "Use `q = q.merge(...)` to create an updated copy." + "QueryParams are immutable since 0.18.0. " "Use `q = q.merge(...)` to create an updated copy." ) def __setitem__(self, key: str, value: str) -> None: raise RuntimeError( - "QueryParams are immutable since 0.18.0. " - "Use `q = q.set(key, value)` to create an updated copy." + "QueryParams are immutable since 0.18.0. " "Use `q = q.set(key, value)` to create an updated copy." ) diff --git a/httpx/_utils.py b/httpx/_utils.py index 1775b1a1ef..bac2aaad54 100644 --- a/httpx/_utils.py +++ b/httpx/_utils.py @@ -18,12 +18,8 @@ _HTML5_FORM_ENCODING_REPLACEMENTS = {'"': "%22", "\\": "\\\\"} -_HTML5_FORM_ENCODING_REPLACEMENTS.update( - {chr(c): "%{:02X}".format(c) for c in range(0x1F + 1) if c != 0x1B} -) -_HTML5_FORM_ENCODING_RE = re.compile( - r"|".join([re.escape(c) for c in _HTML5_FORM_ENCODING_REPLACEMENTS.keys()]) -) +_HTML5_FORM_ENCODING_REPLACEMENTS.update({chr(c): "%{:02X}".format(c) for c in range(0x1F + 1) if c != 0x1B}) +_HTML5_FORM_ENCODING_RE = re.compile(r"|".join([re.escape(c) for c in _HTML5_FORM_ENCODING_REPLACEMENTS.keys()])) def normalize_header_key( @@ -42,9 +38,7 @@ def normalize_header_key( return bytes_value.lower() if lower else bytes_value -def normalize_header_value( - value: typing.Union[str, bytes], encoding: typing.Optional[str] = None -) -> bytes: +def normalize_header_value(value: typing.Union[str, bytes], encoding: typing.Optional[str] = None) -> bytes: """ Coerce str/bytes into a strictly byte-wise HTTP header value. """ @@ -205,11 +199,7 @@ def same_origin(url: "URL", other: "URL") -> bool: """ Return 'True' if the given URLs share the same origin. """ - return ( - url.scheme == other.scheme - and url.host == other.host - and port_or_default(url) == port_or_default(other) - ) + return url.scheme == other.scheme and url.host == other.host and port_or_default(url) == port_or_default(other) def is_https_redirect(url: "URL", location: "URL") -> bool: @@ -240,9 +230,7 @@ def get_environment_proxies() -> typing.Dict[str, typing.Optional[str]]: for scheme in ("http", "https", "all"): if proxy_info.get(scheme): hostname = proxy_info[scheme] - mounts[f"{scheme}://"] = ( - hostname if "://" in hostname else f"http://{hostname}" - ) + mounts[f"{scheme}://"] = hostname if "://" in hostname else f"http://{hostname}" no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")] for hostname in no_proxy_hosts: @@ -427,11 +415,7 @@ def __init__(self, pattern: str) -> None: def matches(self, other: "URL") -> bool: if self.scheme and self.scheme != other.scheme: return False - if ( - self.host - and self.host_regex is not None - and not self.host_regex.match(other.host) - ): + if self.host and self.host_regex is not None and not self.host_regex.match(other.host): return False if self.port is not None and self.port != other.port: return False diff --git a/pyproject.toml b/pyproject.toml index 7f2b6834a7..09e0c47c32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,8 +94,8 @@ replacement = 'src="https://raw.githubusercontent.com/encode/httpx/master/\1"' # https://beta.ruff.rs/docs/configuration/#using-rufftoml [tool.ruff] select = ["E", "F", "I", "B", "PIE"] -ignore = ["B904", "B028", "E501"] -line-length = 88 +ignore = ["B904", "B028"] +line-length = 120 [tool.ruff.isort] diff --git a/tests/client/test_async_client.py b/tests/client/test_async_client.py index cc19e93d41..ed274e346b 100644 --- a/tests/client/test_async_client.py +++ b/tests/client/test_async_client.py @@ -113,9 +113,7 @@ def hello_world() -> typing.Iterator[bytes]: # pragma: no cover async def test_raise_for_status(server): async with httpx.AsyncClient() as client: for status_code in (200, 400, 404, 500, 505): - response = await client.request( - "GET", server.url.copy_with(path=f"/status/{status_code}") - ) + response = await client.request("GET", server.url.copy_with(path=f"/status/{status_code}")) if 400 <= status_code < 600: with pytest.raises(httpx.HTTPStatusError) as exc_info: @@ -169,9 +167,7 @@ async def test_100_continue(server): content = b"Echo request body" async with httpx.AsyncClient() as client: - response = await client.post( - server.url.copy_with(path="/echo_body"), headers=headers, content=content - ) + response = await client.post(server.url.copy_with(path="/echo_body"), headers=headers, content=content) assert response.status_code == 200 assert response.content == content @@ -233,9 +229,7 @@ async def __aexit__(self, *args): transport = Transport(name="transport") mounted = Transport(name="mounted") - async with httpx.AsyncClient( - transport=transport, mounts={"http://www.example.org": mounted} - ): + async with httpx.AsyncClient(transport=transport, mounts={"http://www.example.org": mounted}): pass assert transport.events == [ @@ -352,9 +346,7 @@ async def aclose(self) -> None: nonlocal stream_was_closed stream_was_closed = True - return httpx.Response( - 200, headers={"Content-Length": "12"}, stream=CancelledStream() - ) + return httpx.Response(200, headers={"Content-Length": "12"}, stream=CancelledStream()) transport = httpx.MockTransport(response_with_cancel_during_stream) diff --git a/tests/client/test_auth.py b/tests/client/test_auth.py index fee515058b..d55bce52bc 100644 --- a/tests/client/test_auth.py +++ b/tests/client/test_auth.py @@ -65,17 +65,11 @@ def challenge_send(self, request: httpx.Request) -> httpx.Response: challenge_data = { "nonce": nonce, "qop": self.qop, - "opaque": ( - "ee6378f3ee14ebfd2fff54b70a91a7c9390518047f242ab2271380db0e14bda1" - ), + "opaque": ("ee6378f3ee14ebfd2fff54b70a91a7c9390518047f242ab2271380db0e14bda1"), "algorithm": self.algorithm, "stale": "FALSE", } - challenge_str = ", ".join( - '{}="{}"'.format(key, value) - for key, value in challenge_data.items() - if value - ) + challenge_str = ", ".join('{}="{}"'.format(key, value) for key, value in challenge_data.items() if value) headers = { "www-authenticate": f'Digest realm="httpx@example.org", {challenge_str}', @@ -96,9 +90,7 @@ class RepeatAuth(httpx.Auth): def __init__(self, repeat: int): self.repeat = repeat - def auth_flow( - self, request: httpx.Request - ) -> typing.Generator[httpx.Request, httpx.Response, None]: + def auth_flow(self, request: httpx.Request) -> typing.Generator[httpx.Request, httpx.Response, None]: nonces = [] for index in range(self.repeat): @@ -123,9 +115,7 @@ class ResponseBodyAuth(httpx.Auth): def __init__(self, token: str) -> None: self.token = token - def auth_flow( - self, request: httpx.Request - ) -> typing.Generator[httpx.Request, httpx.Response, None]: + def auth_flow(self, request: httpx.Request) -> typing.Generator[httpx.Request, httpx.Response, None]: request.headers["Authorization"] = self.token response = yield request data = response.text @@ -143,16 +133,12 @@ def __init__(self) -> None: self._lock = threading.Lock() self._async_lock = anyio.Lock() - def sync_auth_flow( - self, request: httpx.Request - ) -> typing.Generator[httpx.Request, httpx.Response, None]: + def sync_auth_flow(self, request: httpx.Request) -> typing.Generator[httpx.Request, httpx.Response, None]: with self._lock: request.headers["Authorization"] = "sync-auth" yield request - async def async_auth_flow( - self, request: httpx.Request - ) -> typing.AsyncGenerator[httpx.Request, httpx.Response]: + async def async_auth_flow(self, request: httpx.Request) -> typing.AsyncGenerator[httpx.Request, httpx.Response]: async with self._async_lock: request.headers["Authorization"] = "async-auth" yield request @@ -180,9 +166,7 @@ async def test_basic_auth_with_stream() -> None: auth = ("user", "password123") app = App() - async with httpx.AsyncClient( - transport=httpx.MockTransport(app), auth=auth - ) as client: + async with httpx.AsyncClient(transport=httpx.MockTransport(app), auth=auth) as client: async with client.stream("GET", url) as response: await response.aread() @@ -208,9 +192,7 @@ async def test_basic_auth_on_session() -> None: auth = ("user", "password123") app = App() - async with httpx.AsyncClient( - transport=httpx.MockTransport(app), auth=auth - ) as client: + async with httpx.AsyncClient(transport=httpx.MockTransport(app), auth=auth) as client: response = await client.get(url) assert response.status_code == 200 @@ -247,9 +229,7 @@ def test_netrc_auth_credentials_exist() -> None: response = client.get(url) assert response.status_code == 200 - assert response.json() == { - "auth": "Basic ZXhhbXBsZS11c2VybmFtZTpleGFtcGxlLXBhc3N3b3Jk" - } + assert response.json() == {"auth": "Basic ZXhhbXBsZS11c2VybmFtZTpleGFtcGxlLXBhc3N3b3Jk"} def test_netrc_auth_credentials_do_not_exist() -> None: @@ -313,9 +293,7 @@ async def test_auth_disable_per_request() -> None: auth = ("user", "password123") app = App() - async with httpx.AsyncClient( - transport=httpx.MockTransport(app), auth=auth - ) as client: + async with httpx.AsyncClient(transport=httpx.MockTransport(app), auth=auth) as client: response = await client.get(url, auth=None) assert response.status_code == 200 @@ -446,9 +424,7 @@ async def test_digest_auth_401_response_without_digest_auth_header() -> None: ], ) @pytest.mark.anyio -async def test_digest_auth( - algorithm: str, expected_hash_length: int, expected_response_length: int -) -> None: +async def test_digest_auth(algorithm: str, expected_hash_length: int, expected_response_length: int) -> None: url = "https://example.org/" auth = httpx.DigestAuth(username="user", password="password123") app = DigestApp(algorithm=algorithm) @@ -586,12 +562,8 @@ async def test_digest_auth_resets_nonce_count_after_401() -> None: assert response_1.status_code == 200 assert len(response_1.history) == 1 - first_nonce = parse_keqv_list( - response_1.request.headers["Authorization"].split(", ") - )["nonce"] - first_nc = parse_keqv_list( - response_1.request.headers["Authorization"].split(", ") - )["nc"] + first_nonce = parse_keqv_list(response_1.request.headers["Authorization"].split(", "))["nonce"] + first_nc = parse_keqv_list(response_1.request.headers["Authorization"].split(", "))["nc"] # with this we now force a 401 on a subsequent (but initial) request app.send_response_after_attempt = 2 @@ -601,17 +573,11 @@ async def test_digest_auth_resets_nonce_count_after_401() -> None: assert response_2.status_code == 200 assert len(response_2.history) == 1 - second_nonce = parse_keqv_list( - response_2.request.headers["Authorization"].split(", ") - )["nonce"] - second_nc = parse_keqv_list( - response_2.request.headers["Authorization"].split(", ") - )["nc"] + second_nonce = parse_keqv_list(response_2.request.headers["Authorization"].split(", "))["nonce"] + second_nc = parse_keqv_list(response_2.request.headers["Authorization"].split(", "))["nc"] assert first_nonce != second_nonce # ensures that the auth challenge was reset - assert ( - first_nc == second_nc - ) # ensures the nonce count is reset when the authentication failed + assert first_nc == second_nc # ensures the nonce count is reset when the authentication failed @pytest.mark.parametrize( diff --git a/tests/client/test_client.py b/tests/client/test_client.py index b8245288ad..26c4fa3bbb 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -132,9 +132,7 @@ async def hello_world() -> typing.AsyncIterator[bytes]: # pragma: no cover def test_raise_for_status(server): with httpx.Client() as client: for status_code in (200, 400, 404, 500, 505): - response = client.request( - "GET", server.url.copy_with(path=f"/status/{status_code}") - ) + response = client.request("GET", server.url.copy_with(path=f"/status/{status_code}")) if 400 <= status_code < 600: with pytest.raises(httpx.HTTPStatusError) as exc_info: response.raise_for_status() @@ -332,10 +330,7 @@ def test_client_closed_state_using_with_block(): def echo_raw_headers(request: httpx.Request) -> httpx.Response: - data = [ - (name.decode("ascii"), value.decode("ascii")) - for name, value in request.headers.raw - ] + data = [(name.decode("ascii"), value.decode("ascii")) for name, value in request.headers.raw] return httpx.Response(200, json=data) @@ -346,9 +341,7 @@ def test_raw_client_header(): url = "http://example.org/echo_headers" headers = {"Example-Header": "example-value"} - client = httpx.Client( - transport=httpx.MockTransport(echo_raw_headers), headers=headers - ) + client = httpx.Client(transport=httpx.MockTransport(echo_raw_headers), headers=headers) response = client.get(url) assert response.status_code == 200 diff --git a/tests/client/test_cookies.py b/tests/client/test_cookies.py index f0c8352593..6e67c0942a 100644 --- a/tests/client/test_cookies.py +++ b/tests/client/test_cookies.py @@ -22,9 +22,7 @@ def test_set_cookie() -> None: url = "http://example.org/echo_cookies" cookies = {"example-name": "example-value"} - client = httpx.Client( - cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) - ) + client = httpx.Client(cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies)) response = client.get(url) assert response.status_code == 200 @@ -74,9 +72,7 @@ def test_set_cookie_with_cookiejar() -> None: ) cookies.set_cookie(cookie) - client = httpx.Client( - cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) - ) + client = httpx.Client(cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies)) response = client.get(url) assert response.status_code == 200 @@ -111,9 +107,7 @@ def test_setting_client_cookies_to_cookiejar() -> None: ) cookies.set_cookie(cookie) - client = httpx.Client( - cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies) - ) + client = httpx.Client(cookies=cookies, transport=httpx.MockTransport(get_and_set_cookies)) response = client.get(url) assert response.status_code == 200 diff --git a/tests/client/test_event_hooks.py b/tests/client/test_event_hooks.py index 6604dd31a3..356acece20 100644 --- a/tests/client/test_event_hooks.py +++ b/tests/client/test_event_hooks.py @@ -24,9 +24,7 @@ def on_response(response): event_hooks = {"request": [on_request], "response": [on_response]} - with httpx.Client( - event_hooks=event_hooks, transport=httpx.MockTransport(app) - ) as http: + with httpx.Client(event_hooks=event_hooks, transport=httpx.MockTransport(app)) as http: http.get("http://127.0.0.1:8000/", auth=("username", "password")) assert events == [ @@ -54,9 +52,7 @@ def raise_on_4xx_5xx(response): event_hooks = {"response": [raise_on_4xx_5xx]} - with httpx.Client( - event_hooks=event_hooks, transport=httpx.MockTransport(app) - ) as http: + with httpx.Client(event_hooks=event_hooks, transport=httpx.MockTransport(app)) as http: try: http.get("http://127.0.0.1:8000/status/400") except httpx.HTTPStatusError as exc: @@ -75,9 +71,7 @@ async def on_response(response): event_hooks = {"request": [on_request], "response": [on_response]} - async with httpx.AsyncClient( - event_hooks=event_hooks, transport=httpx.MockTransport(app) - ) as http: + async with httpx.AsyncClient(event_hooks=event_hooks, transport=httpx.MockTransport(app)) as http: await http.get("http://127.0.0.1:8000/", auth=("username", "password")) assert events == [ @@ -106,9 +100,7 @@ async def raise_on_4xx_5xx(response): event_hooks = {"response": [raise_on_4xx_5xx]} - async with httpx.AsyncClient( - event_hooks=event_hooks, transport=httpx.MockTransport(app) - ) as http: + async with httpx.AsyncClient(event_hooks=event_hooks, transport=httpx.MockTransport(app)) as http: try: await http.get("http://127.0.0.1:8000/status/400") except httpx.HTTPStatusError as exc: diff --git a/tests/client/test_headers.py b/tests/client/test_headers.py index 264ca0bd67..7aa24fc5c3 100755 --- a/tests/client/test_headers.py +++ b/tests/client/test_headers.py @@ -47,9 +47,7 @@ def test_header_merge(): url = "http://example.org/echo_headers" client_headers = {"User-Agent": "python-myclient/0.2.1"} request_headers = {"X-Auth-Token": "FooBarBazToken"} - client = httpx.Client( - transport=httpx.MockTransport(echo_headers), headers=client_headers - ) + client = httpx.Client(transport=httpx.MockTransport(echo_headers), headers=client_headers) response = client.get(url, headers=request_headers) assert response.status_code == 200 @@ -69,9 +67,7 @@ def test_header_merge_conflicting_headers(): url = "http://example.org/echo_headers" client_headers = {"X-Auth-Token": "FooBar"} request_headers = {"X-Auth-Token": "BazToken"} - client = httpx.Client( - transport=httpx.MockTransport(echo_headers), headers=client_headers - ) + client = httpx.Client(transport=httpx.MockTransport(echo_headers), headers=client_headers) response = client.get(url, headers=request_headers) assert response.status_code == 200 @@ -91,9 +87,7 @@ def test_header_update(): url = "http://example.org/echo_headers" client = httpx.Client(transport=httpx.MockTransport(echo_headers)) first_response = client.get(url) - client.headers.update( - {"User-Agent": "python-myclient/0.2.1", "Another-Header": "AThing"} - ) + client.headers.update({"User-Agent": "python-myclient/0.2.1", "Another-Header": "AThing"}) second_response = client.get(url) assert first_response.status_code == 200 @@ -130,16 +124,12 @@ def test_header_repeated_items(): echoed_headers = response.json()["headers"] # as per RFC 7230, the whitespace after a comma is insignificant # so we split and strip here so that we can do a safe comparison - assert ["x-header", ["1", "2", "3"]] in [ - [k, [subv.lstrip() for subv in v.split(",")]] for k, v in echoed_headers - ] + assert ["x-header", ["1", "2", "3"]] in [[k, [subv.lstrip() for subv in v.split(",")]] for k, v in echoed_headers] def test_header_repeated_multi_items(): url = "http://example.org/echo_headers" - client = httpx.Client( - transport=httpx.MockTransport(echo_repeated_headers_multi_items) - ) + client = httpx.Client(transport=httpx.MockTransport(echo_repeated_headers_multi_items)) response = client.get(url, headers=[("x-header", "1"), ("x-header", "2,3")]) assert response.status_code == 200 diff --git a/tests/client/test_queryparams.py b/tests/client/test_queryparams.py index e5acb0ba20..d3d99645f4 100644 --- a/tests/client/test_queryparams.py +++ b/tests/client/test_queryparams.py @@ -26,9 +26,7 @@ def test_client_queryparams_echo(): url = "http://example.org/echo_queryparams" client_queryparams = "first=str" request_queryparams = {"second": "dict"} - client = httpx.Client( - transport=httpx.MockTransport(hello_world), params=client_queryparams - ) + client = httpx.Client(transport=httpx.MockTransport(hello_world), params=client_queryparams) response = client.get(url, params=request_queryparams) assert response.status_code == 200 diff --git a/tests/client/test_redirects.py b/tests/client/test_redirects.py index 6155df1447..883ac416ac 100644 --- a/tests/client/test_redirects.py +++ b/tests/client/test_redirects.py @@ -181,9 +181,7 @@ def test_head_redirect(): def test_relative_redirect(): client = httpx.Client(transport=httpx.MockTransport(redirects)) - response = client.get( - "https://example.org/relative_redirect", follow_redirects=True - ) + response = client.get("https://example.org/relative_redirect", follow_redirects=True) assert response.status_code == httpx.codes.OK assert response.url == "https://example.org/" assert len(response.history) == 1 @@ -192,9 +190,7 @@ def test_relative_redirect(): def test_malformed_redirect(): # https://github.com/encode/httpx/issues/771 client = httpx.Client(transport=httpx.MockTransport(redirects)) - response = client.get( - "http://example.org/malformed_redirect", follow_redirects=True - ) + response = client.get("http://example.org/malformed_redirect", follow_redirects=True) assert response.status_code == httpx.codes.OK assert response.url == "https://example.org:443/" assert len(response.history) == 1 @@ -208,9 +204,7 @@ def test_invalid_redirect(): def test_no_scheme_redirect(): client = httpx.Client(transport=httpx.MockTransport(redirects)) - response = client.get( - "https://example.org/no_scheme_redirect", follow_redirects=True - ) + response = client.get("https://example.org/no_scheme_redirect", follow_redirects=True) assert response.status_code == httpx.codes.OK assert response.url == "https://example.org/" assert len(response.history) == 1 @@ -218,9 +212,7 @@ def test_no_scheme_redirect(): def test_fragment_redirect(): client = httpx.Client(transport=httpx.MockTransport(redirects)) - response = client.get( - "https://example.org/relative_redirect#fragment", follow_redirects=True - ) + response = client.get("https://example.org/relative_redirect#fragment", follow_redirects=True) assert response.status_code == httpx.codes.OK assert response.url == "https://example.org/#fragment" assert len(response.history) == 1 @@ -228,9 +220,7 @@ def test_fragment_redirect(): def test_multiple_redirects(): client = httpx.Client(transport=httpx.MockTransport(redirects)) - response = client.get( - "https://example.org/multiple_redirects?count=20", follow_redirects=True - ) + response = client.get("https://example.org/multiple_redirects?count=20", follow_redirects=True) assert response.status_code == httpx.codes.OK assert response.url == "https://example.org/multiple_redirects" assert len(response.history) == 20 @@ -244,17 +234,13 @@ def test_multiple_redirects(): async def test_async_too_many_redirects(): async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client: with pytest.raises(httpx.TooManyRedirects): - await client.get( - "https://example.org/multiple_redirects?count=21", follow_redirects=True - ) + await client.get("https://example.org/multiple_redirects?count=21", follow_redirects=True) def test_sync_too_many_redirects(): client = httpx.Client(transport=httpx.MockTransport(redirects)) with pytest.raises(httpx.TooManyRedirects): - client.get( - "https://example.org/multiple_redirects?count=21", follow_redirects=True - ) + client.get("https://example.org/multiple_redirects?count=21", follow_redirects=True) def test_redirect_loop(): @@ -380,10 +366,7 @@ def cookie_sessions(request: httpx.Request) -> httpx.Response: status_code = httpx.codes.SEE_OTHER headers = { "location": "/", - "set-cookie": ( - "session=eyJ1c2VybmFtZSI6ICJ0b21; path=/; Max-Age=1209600; " - "httponly; samesite=lax" - ), + "set-cookie": ("session=eyJ1c2VybmFtZSI6ICJ0b21; path=/; Max-Age=1209600; " "httponly; samesite=lax"), } return httpx.Response(status_code, headers=headers) @@ -392,18 +375,13 @@ def cookie_sessions(request: httpx.Request) -> httpx.Response: status_code = httpx.codes.SEE_OTHER headers = { "location": "/", - "set-cookie": ( - "session=null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; " - "httponly; samesite=lax" - ), + "set-cookie": ("session=null; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; " "httponly; samesite=lax"), } return httpx.Response(status_code, headers=headers) def test_redirect_cookie_behavior(): - client = httpx.Client( - transport=httpx.MockTransport(cookie_sessions), follow_redirects=True - ) + client = httpx.Client(transport=httpx.MockTransport(cookie_sessions), follow_redirects=True) # The client is not logged in. response = client.get("https://example.com/") @@ -442,6 +420,4 @@ def test_redirect_custom_scheme(): async def test_async_invalid_redirect(): async with httpx.AsyncClient(transport=httpx.MockTransport(redirects)) as client: with pytest.raises(httpx.RemoteProtocolError): - await client.get( - "http://example.org/invalid_redirect", follow_redirects=True - ) + await client.get("http://example.org/invalid_redirect", follow_redirects=True) diff --git a/tests/conftest.py b/tests/conftest.py index 1bcb6a4220..b27e12a009 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,9 +50,7 @@ def clean_environ(): Message = typing.Dict[str, typing.Any] Receive = typing.Callable[[], typing.Awaitable[Message]] -Send = typing.Callable[ - [typing.Dict[str, typing.Any]], typing.Coroutine[None, None, None] -] +Send = typing.Callable[[typing.Dict[str, typing.Any]], typing.Coroutine[None, None, None]] Scope = typing.Dict[str, typing.Any] @@ -161,10 +159,7 @@ async def echo_binary(scope: Scope, receive: Receive, send: Send) -> None: async def echo_headers(scope: Scope, receive: Receive, send: Send) -> None: - body = { - name.capitalize().decode(): value.decode() - for name, value in scope.get("headers", []) - } + body = {name.capitalize().decode(): value.decode() for name, value in scope.get("headers", [])} await send( { "type": "http.response.start", @@ -176,9 +171,7 @@ async def echo_headers(scope: Scope, receive: Receive, send: Send) -> None: async def redirect_301(scope: Scope, receive: Receive, send: Send) -> None: - await send( - {"type": "http.response.start", "status": 301, "headers": [[b"location", b"/"]]} - ) + await send({"type": "http.response.start", "status": 301, "headers": [[b"location", b"/"]]}) await send({"type": "http.response.body"}) @@ -213,9 +206,7 @@ def cert_private_key_file(localhost_cert): @pytest.fixture(scope="session") def cert_encrypted_private_key_file(localhost_cert): # Deserialize the private key and then reserialize with a password - private_key = load_pem_private_key( - localhost_cert.private_key_pem.bytes(), password=None, backend=default_backend() - ) + private_key = load_pem_private_key(localhost_cert.private_key_pem.bytes(), password=None, backend=default_backend()) encrypted_private_key_pem = trustme.Blob( private_key.private_bytes( Encoding.PEM, diff --git a/tests/models/test_cookies.py b/tests/models/test_cookies.py index dbe1bfb99e..ff6db27b0d 100644 --- a/tests/models/test_cookies.py +++ b/tests/models/test_cookies.py @@ -57,8 +57,7 @@ def test_multiple_set_cookie(): headers = [ ( b"Set-Cookie", - b"1P_JAR=2020-08-09-18; expires=Tue, 08-Sep-2099 18:33:35 GMT; " - b"path=/; domain=.example.org; Secure", + b"1P_JAR=2020-08-09-18; expires=Tue, 08-Sep-2099 18:33:35 GMT; " b"path=/; domain=.example.org; Secure", ), ( b"Set-Cookie", @@ -93,6 +92,5 @@ def test_cookies_repr(): cookies.set(name="fizz", value="buzz", domain="http://hello.com") assert ( - repr(cookies) - == ", ]>" + repr(cookies) == ", ]>" ) diff --git a/tests/models/test_headers.py b/tests/models/test_headers.py index d671dc4186..f11de2c5ed 100644 --- a/tests/models/test_headers.py +++ b/tests/models/test_headers.py @@ -108,9 +108,7 @@ def test_headers_list_repr(): Headers should display with a list repr if they include multiple identical keys. """ headers = httpx.Headers([("custom", "example 1"), ("custom", "example 2")]) - assert ( - repr(headers) == "Headers([('custom', 'example 1'), ('custom', 'example 2')])" - ) + assert repr(headers) == "Headers([('custom', 'example 1'), ('custom', 'example 2')])" def test_headers_decode_ascii(): diff --git a/tests/models/test_queryparams.py b/tests/models/test_queryparams.py index 29b2ca634d..65d1375090 100644 --- a/tests/models/test_queryparams.py +++ b/tests/models/test_queryparams.py @@ -31,19 +31,11 @@ def test_queryparams(source): assert dict(q) == {"a": "123", "b": "789"} assert str(q) == "a=123&a=456&b=789" assert repr(q) == "QueryParams('a=123&a=456&b=789')" - assert httpx.QueryParams({"a": "123", "b": "456"}) == httpx.QueryParams( - [("a", "123"), ("b", "456")] - ) - assert httpx.QueryParams({"a": "123", "b": "456"}) == httpx.QueryParams( - "a=123&b=456" - ) - assert httpx.QueryParams({"a": "123", "b": "456"}) == httpx.QueryParams( - {"b": "456", "a": "123"} - ) + assert httpx.QueryParams({"a": "123", "b": "456"}) == httpx.QueryParams([("a", "123"), ("b", "456")]) + assert httpx.QueryParams({"a": "123", "b": "456"}) == httpx.QueryParams("a=123&b=456") + assert httpx.QueryParams({"a": "123", "b": "456"}) == httpx.QueryParams({"b": "456", "a": "123"}) assert httpx.QueryParams() == httpx.QueryParams({}) - assert httpx.QueryParams([("a", "123"), ("a", "456")]) == httpx.QueryParams( - "a=123&a=456" - ) + assert httpx.QueryParams([("a", "123"), ("a", "456")]) == httpx.QueryParams("a=123&a=456") assert httpx.QueryParams({"a": "123", "b": "456"}) != "invalid" q = httpx.QueryParams([("a", "123"), ("a", "456")]) diff --git a/tests/models/test_requests.py b/tests/models/test_requests.py index d0d4f11d32..514520684e 100644 --- a/tests/models/test_requests.py +++ b/tests/models/test_requests.py @@ -43,9 +43,7 @@ def content() -> typing.Iterator[bytes]: yield b"test 123" # pragma: no cover headers = {"Content-Length": "8"} - request = httpx.Request( - "POST", "http://example.org", content=content(), headers=headers - ) + request = httpx.Request("POST", "http://example.org", content=content(), headers=headers) assert request.headers == {"Host": "example.org", "Content-Length": "8"} diff --git a/tests/models/test_responses.py b/tests/models/test_responses.py index 9177773a50..45396ca27a 100644 --- a/tests/models/test_responses.py +++ b/tests/models/test_responses.py @@ -237,9 +237,7 @@ def test_response_no_charset_with_iso_8859_1_content(): """ content = "Accented: Österreich abcdefghijklmnopqrstuzwxyz".encode("iso-8859-1") headers = {"Content-Type": "text/plain"} - response = httpx.Response( - 200, content=content, headers=headers, default_encoding=autodetect - ) + response = httpx.Response(200, content=content, headers=headers, default_encoding=autodetect) assert response.text == "Accented: Österreich abcdefghijklmnopqrstuzwxyz" assert response.charset_encoding is None @@ -251,9 +249,7 @@ def test_response_no_charset_with_cp_1252_content(): """ content = "Euro Currency: € abcdefghijklmnopqrstuzwxyz".encode("cp1252") headers = {"Content-Type": "text/plain"} - response = httpx.Response( - 200, content=content, headers=headers, default_encoding=autodetect - ) + response = httpx.Response(200, content=content, headers=headers, default_encoding=autodetect) assert response.text == "Euro Currency: € abcdefghijklmnopqrstuzwxyz" assert response.charset_encoding is None @@ -273,9 +269,7 @@ def test_response_non_text_encoding(): def test_response_set_explicit_encoding(): - headers = { - "Content-Type": "text-plain; charset=utf-8" - } # Deliberately incorrect charset + headers = {"Content-Type": "text-plain; charset=utf-8"} # Deliberately incorrect charset response = httpx.Response( 200, content="Latin 1: ÿ".encode("latin-1"), diff --git a/tests/models/test_url.py b/tests/models/test_url.py index a47205f97d..99e1c5dfc7 100644 --- a/tests/models/test_url.py +++ b/tests/models/test_url.py @@ -82,9 +82,7 @@ def test_url(): assert url.query == b"abc=123" assert url.raw_path == b"/path/to/somewhere?abc=123" assert url.fragment == "anchor" - assert ( - repr(url) == "URL('https://example.org:123/path/to/somewhere?abc=123#anchor')" - ) + assert repr(url) == "URL('https://example.org:123/path/to/somewhere?abc=123#anchor')" new = url.copy_with(scheme="http", port=None) assert new == httpx.URL("http://example.org/path/to/somewhere?abc=123#anchor") @@ -102,9 +100,7 @@ def test_url_params(): assert str(url) == "https://example.org:123/path/to/somewhere?a=123" assert url.params == httpx.QueryParams({"a": "123"}) - url = httpx.URL( - "https://example.org:123/path/to/somewhere?b=456", params={"a": "123"} - ) + url = httpx.URL("https://example.org:123/path/to/somewhere?b=456", params={"a": "123"}) assert str(url) == "https://example.org:123/path/to/somewhere?a=123" assert url.params == httpx.QueryParams({"a": "123"}) @@ -115,12 +111,8 @@ def test_url_join(): """ url = httpx.URL("https://example.org:123/path/to/somewhere") assert url.join("/somewhere-else") == "https://example.org:123/somewhere-else" - assert ( - url.join("somewhere-else") == "https://example.org:123/path/to/somewhere-else" - ) - assert ( - url.join("../somewhere-else") == "https://example.org:123/path/somewhere-else" - ) + assert url.join("somewhere-else") == "https://example.org:123/path/to/somewhere-else" + assert url.join("../somewhere-else") == "https://example.org:123/path/somewhere-else" assert url.join("../../somewhere-else") == "https://example.org:123/somewhere-else" diff --git a/tests/test_asgi.py b/tests/test_asgi.py index 14d6df6ded..fcd6ce25bf 100644 --- a/tests/test_asgi.py +++ b/tests/test_asgi.py @@ -48,9 +48,7 @@ async def echo_body(scope, receive, send): async def echo_headers(scope, receive, send): status = 200 - output = json.dumps( - {"headers": [[k.decode(), v.decode()] for k, v in scope["headers"]]} - ).encode("utf-8") + output = json.dumps({"headers": [[k.decode(), v.decode()] for k, v in scope["headers"]]}).encode("utf-8") headers = [(b"content-type", "text/plain"), (b"content-length", str(len(output)))] await send({"type": "http.response.start", "status": status, "headers": headers}) @@ -170,9 +168,7 @@ async def read_body(scope, receive, send): status = 200 headers = [(b"content-type", "text/plain")] - await send( - {"type": "http.response.start", "status": status, "headers": headers} - ) + await send({"type": "http.response.start", "status": status, "headers": headers}) more_body = True while more_body: message = await receive() diff --git a/tests/test_auth.py b/tests/test_auth.py index 563256954d..516837c1aa 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -50,12 +50,8 @@ def test_digest_auth_with_401(): assert "Authorization" not in request.headers # If a 401 response is returned, then a digest auth request is made. - headers = { - "WWW-Authenticate": 'Digest realm="...", qop="auth", nonce="...", opaque="..."' - } - response = httpx.Response( - content=b"Auth required", status_code=401, headers=headers, request=request - ) + headers = {"WWW-Authenticate": 'Digest realm="...", qop="auth", nonce="...", opaque="..."'} + response = httpx.Response(content=b"Auth required", status_code=401, headers=headers, request=request) request = flow.send(response) assert request.headers["Authorization"].startswith("Digest") @@ -75,12 +71,8 @@ def test_digest_auth_with_401_nonce_counting(): assert "Authorization" not in request.headers # If a 401 response is returned, then a digest auth request is made. - headers = { - "WWW-Authenticate": 'Digest realm="...", qop="auth", nonce="...", opaque="..."' - } - response = httpx.Response( - content=b"Auth required", status_code=401, headers=headers, request=request - ) + headers = {"WWW-Authenticate": 'Digest realm="...", qop="auth", nonce="...", opaque="..."'} + response = httpx.Response(content=b"Auth required", status_code=401, headers=headers, request=request) first_request = flow.send(response) assert first_request.headers["Authorization"].startswith("Digest") @@ -92,9 +84,7 @@ def test_digest_auth_with_401_nonce_counting(): # ... and the client nonce count (nc) is increased first_nc = parse_keqv_list(first_request.headers["Authorization"].split(", "))["nc"] - second_nc = parse_keqv_list(second_request.headers["Authorization"].split(", "))[ - "nc" - ] + second_nc = parse_keqv_list(second_request.headers["Authorization"].split(", "))["nc"] assert int(first_nc, 16) + 1 == int(second_nc, 16) # No other requests are made. @@ -109,9 +99,7 @@ def set_cookies(request: httpx.Request) -> httpx.Response: "WWW-Authenticate": 'Digest realm="...", qop="auth", nonce="...", opaque="..."', } if request.url.path == "/auth": - return httpx.Response( - content=b"Auth required", status_code=401, headers=headers - ) + return httpx.Response(content=b"Auth required", status_code=401, headers=headers) else: raise NotImplementedError() # pragma: no cover @@ -135,8 +123,6 @@ def test_digest_auth_setting_cookie_in_request(): assert request.headers["Cookie"] == "session=.session_value..." # No other requests are made. - response = httpx.Response( - content=b"Hello, world!", status_code=200, request=request - ) + response = httpx.Response(content=b"Hello, world!", status_code=200, request=request) with pytest.raises(StopIteration): flow.send(response) diff --git a/tests/test_config.py b/tests/test_config.py index 00913b2c17..be85d3372a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -26,14 +26,8 @@ def test_load_ssl_config_verify_existing_file(): @pytest.mark.parametrize("config", ("SSL_CERT_FILE", "SSL_CERT_DIR")) -def test_load_ssl_config_verify_env_file( - https_server, ca_cert_pem_file, config, cert_authority -): - os.environ[config] = ( - ca_cert_pem_file - if config.endswith("_FILE") - else str(Path(ca_cert_pem_file).parent) - ) +def test_load_ssl_config_verify_env_file(https_server, ca_cert_pem_file, config, cert_authority): + os.environ[config] = ca_cert_pem_file if config.endswith("_FILE") else str(Path(ca_cert_pem_file).parent) context = httpx.create_ssl_context(trust_env=True) cert_authority.configure_trust(context) @@ -56,23 +50,15 @@ def test_load_ssl_config_cert_and_key(cert_pem_file, cert_private_key_file): @pytest.mark.parametrize("password", [b"password", "password"]) -def test_load_ssl_config_cert_and_encrypted_key( - cert_pem_file, cert_encrypted_private_key_file, password -): - context = httpx.create_ssl_context( - cert=(cert_pem_file, cert_encrypted_private_key_file, password) - ) +def test_load_ssl_config_cert_and_encrypted_key(cert_pem_file, cert_encrypted_private_key_file, password): + context = httpx.create_ssl_context(cert=(cert_pem_file, cert_encrypted_private_key_file, password)) assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED assert context.check_hostname is True -def test_load_ssl_config_cert_and_key_invalid_password( - cert_pem_file, cert_encrypted_private_key_file -): +def test_load_ssl_config_cert_and_key_invalid_password(cert_pem_file, cert_encrypted_private_key_file): with pytest.raises(ssl.SSLError): - httpx.create_ssl_context( - cert=(cert_pem_file, cert_encrypted_private_key_file, "password1") - ) + httpx.create_ssl_context(cert=(cert_pem_file, cert_encrypted_private_key_file, "password1")) def test_load_ssl_config_cert_without_key_raises(cert_pem_file): diff --git a/tests/test_decoders.py b/tests/test_decoders.py index d71e9a833e..9c41cb7829 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -79,9 +79,7 @@ def test_multi(): compressed_body = deflate_compressor.compress(body) + deflate_compressor.flush() gzip_compressor = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16) - compressed_body = ( - gzip_compressor.compress(compressed_body) + gzip_compressor.flush() - ) + compressed_body = gzip_compressor.compress(compressed_body) + gzip_compressor.flush() headers = [(b"Content-Encoding", b"deflate, gzip")] response = httpx.Response( @@ -229,9 +227,7 @@ def test_line_decoder_nl(): assert list(response.iter_lines()) == ["a", "", "b", "c"] # Issue #1033 - response = httpx.Response( - 200, content=[b"", b"12345\n", b"foo ", b"bar ", b"baz\n"] - ) + response = httpx.Response(200, content=[b"", b"12345\n", b"foo ", b"bar ", b"baz\n"]) assert list(response.iter_lines()) == ["12345", "foo bar baz"] @@ -243,9 +239,7 @@ def test_line_decoder_cr(): assert list(response.iter_lines()) == ["a", "", "b", "c"] # Issue #1033 - response = httpx.Response( - 200, content=[b"", b"12345\r", b"foo ", b"bar ", b"baz\r"] - ) + response = httpx.Response(200, content=[b"", b"12345\r", b"foo ", b"bar ", b"baz\r"]) assert list(response.iter_lines()) == ["12345", "foo bar baz"] diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 6547ab37a5..def18dbb1c 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -17,15 +17,11 @@ def test_httpcore_all_exceptions_mapped() -> None: expected_mapped_httpcore_exceptions = { value.__name__ for _, value in vars(httpcore).items() - if isinstance(value, type) - and issubclass(value, Exception) - and value is not httpcore.ConnectionNotAvailable + if isinstance(value, type) and issubclass(value, Exception) and value is not httpcore.ConnectionNotAvailable } httpx_exceptions = { - value.__name__ - for _, value in vars(httpx).items() - if isinstance(value, type) and issubclass(value, Exception) + value.__name__ for _, value in vars(httpx).items() if isinstance(value, type) and issubclass(value, Exception) } unmapped_exceptions = expected_mapped_httpcore_exceptions - httpx_exceptions diff --git a/tests/test_exported_members.py b/tests/test_exported_members.py index 8d9c8a74ca..743ed4fb26 100644 --- a/tests/test_exported_members.py +++ b/tests/test_exported_members.py @@ -4,10 +4,6 @@ def test_all_imports_are_exported() -> None: included_private_members = ["__description__", "__title__", "__version__"] assert httpx.__all__ == sorted( - ( - member - for member in vars(httpx).keys() - if not member.startswith("_") or member in included_private_members - ), + (member for member in vars(httpx).keys() if not member.startswith("_") or member in included_private_members), key=str.casefold, ) diff --git a/tests/test_multipart.py b/tests/test_multipart.py index 55211fb1a9..95566594a9 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -292,9 +292,7 @@ def test_multipart_encode_files_allows_filenames_as_none() -> None: ("no-extension", "application/octet-stream"), ], ) -def test_multipart_encode_files_guesses_correct_content_type( - file_name: str, expected_content_type: str -) -> None: +def test_multipart_encode_files_guesses_correct_content_type(file_name: str, expected_content_type: str) -> None: url = "https://www.example.com/" headers = {"Content-Type": "multipart/form-data; boundary=BOUNDARY"} files = {"file": (file_name, io.BytesIO(b""))} diff --git a/tests/test_urlparse.py b/tests/test_urlparse.py index b03291b44f..0fa97c60f0 100644 --- a/tests/test_urlparse.py +++ b/tests/test_urlparse.py @@ -174,10 +174,7 @@ def test_urlparse_non_printing_character_in_url(): def test_urlparse_non_printing_character_in_component(): with pytest.raises(httpx.InvalidURL) as exc: httpx.URL("https://www.example.com", path="/\n") - assert ( - str(exc.value) - == "Invalid non-printable ASCII character in URL component 'path'" - ) + assert str(exc.value) == "Invalid non-printable ASCII character in URL component 'path'" # Test for urlparse components @@ -216,17 +213,11 @@ def test_urlparse_with_invalid_path(): with pytest.raises(httpx.InvalidURL) as exc: httpx.URL(path="//abc") - assert ( - str(exc.value) - == "URLs with no authority component cannot have a path starting with '//'" - ) + assert str(exc.value) == "URLs with no authority component cannot have a path starting with '//'" with pytest.raises(httpx.InvalidURL) as exc: httpx.URL(path=":abc") - assert ( - str(exc.value) - == "URLs with no scheme component cannot have a path starting with ':'" - ) + assert str(exc.value) == "URLs with no scheme component cannot have a path starting with ':'" def test_urlparse_with_relative_path(): diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py index a952da6af9..a6fe6d498b 100644 --- a/tests/test_wsgi.py +++ b/tests/test_wsgi.py @@ -28,9 +28,7 @@ def application(environ, start_response): return wsgiref.validate.validator(application) -def echo_body( - environ: "WSGIEnvironment", start_response: "StartResponse" -) -> typing.Iterable[bytes]: +def echo_body(environ: "WSGIEnvironment", start_response: "StartResponse") -> typing.Iterable[bytes]: status = "200 OK" output = environ["wsgi.input"].read()