Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-httpx for openSUSE:Factory checked in at 2023-10-26 17:11:48 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-httpx (Old) and /work/SRC/openSUSE:Factory/.python-httpx.new.24901 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-httpx" Thu Oct 26 17:11:48 2023 rev:11 rq:1120298 version:0.25.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-httpx/python-httpx.changes 2023-09-12 21:02:37.139609768 +0200 +++ /work/SRC/openSUSE:Factory/.python-httpx.new.24901/python-httpx.changes 2023-10-26 17:11:53.418696672 +0200 @@ -1,0 +2,21 @@ +Fri Oct 20 21:52:56 UTC 2023 - Matej Cepl <mc...@cepl.eu> + +- Update to 0.25.0: + - Drop support for Python 3.7. + - Support HTTPS proxies. + - Change the type of Extensions from Mapping[Str, Any] to + MutableMapping[Str, Any]. + - Add socket_options argument to httpx.HTTPTransport and + httpx.AsyncHTTPTransport classes. + - The Response.raise_for_status() method now returns + the response instance. For example: data = + httpx.get('...').raise_for_status().json(). + - Return 500 error response instead of exceptions when + raise_app_exceptions=False is set on ASGITransport. + - Ensure all WSGITransport environs have a SERVER_PROTOCOL. + - Always encode forward slashes as %2F in query parameters + - Use Mozilla documentation instead of httpstatuses.com for + HTTP error reference +- Requires higher version of httpcore. + +------------------------------------------------------------------- Old: ---- httpx-0.24.1.tar.gz New: ---- httpx-0.25.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-httpx.spec ++++++ --- /var/tmp/diff_new_pack.KXq9Sx/_old 2023-10-26 17:11:54.010718415 +0200 +++ /var/tmp/diff_new_pack.KXq9Sx/_new 2023-10-26 17:11:54.010718415 +0200 @@ -27,7 +27,7 @@ %{?sle15_python_module_pythons} Name: python-httpx%{psuffix} -Version: 0.24.1 +Version: 0.25.0 Release: 0 Summary: Python HTTP client with async support License: BSD-3-Clause @@ -43,7 +43,7 @@ Requires: python-certifi Requires: python-idna >= 2.0 Requires: python-sniffio -Requires: (python-httpcore >= 0.17.2 with python-httpcore < 0.18.0) +Requires: (python-httpcore >= 0.18.0 with python-httpcore < 0.19.0) Recommends: python-Brotli Recommends: python-Pygments >= 2 Recommends: python-click >= 8 ++++++ httpx-0.24.1.tar.gz -> httpx-0.25.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/.github/workflows/publish.yml new/httpx-0.25.0/.github/workflows/publish.yml --- old/httpx-0.24.1/.github/workflows/publish.yml 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/.github/workflows/publish.yml 2023-09-11 12:13:24.000000000 +0200 @@ -17,7 +17,7 @@ - uses: "actions/checkout@v3" - uses: "actions/setup-python@v4" with: - python-version: 3.7 + python-version: 3.8 - name: "Install dependencies" run: "scripts/install" - name: "Build package & docs" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/.github/workflows/test-suite.yml new/httpx-0.25.0/.github/workflows/test-suite.yml --- old/httpx-0.24.1/.github/workflows/test-suite.yml 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/.github/workflows/test-suite.yml 2023-09-11 12:13:24.000000000 +0200 @@ -14,7 +14,7 @@ strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - uses: "actions/checkout@v3" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/CHANGELOG.md new/httpx-0.25.0/CHANGELOG.md --- old/httpx-0.24.1/CHANGELOG.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/CHANGELOG.md 2023-09-11 12:13:24.000000000 +0200 @@ -4,6 +4,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 0.25.0 (11th Sep, 2023) + +### Removed + +* Drop support for Python 3.7. (#2813) + +### Added + +* Support HTTPS proxies. (#2845) +* Change the type of `Extensions` from `Mapping[Str, Any]` to `MutableMapping[Str, Any]`. (#2803) +* Add `socket_options` argument to `httpx.HTTPTransport` and `httpx.AsyncHTTPTransport` classes. (#2716) +* The `Response.raise_for_status()` method now returns the response instance. For example: `data = httpx.get('...').raise_for_status().json()`. (#2776) + +### Fixed + +* Return `500` error response instead of exceptions when `raise_app_exceptions=False` is set on `ASGITransport`. (#2669) +* Ensure all `WSGITransport` environs have a `SERVER_PROTOCOL`. (#2708) +* Always encode forward slashes as `%2F` in query parameters (#2723) +* Use Mozilla documentation instead of `httpstatuses.com` for HTTP error reference (#2768) + ## 0.24.1 (17th May, 2023) ### Added diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/README.md new/httpx-0.25.0/README.md --- old/httpx-0.24.1/README.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/README.md 2023-09-11 12:13:24.000000000 +0200 @@ -103,7 +103,7 @@ $ pip install httpx[http2] ``` -HTTPX requires Python 3.7+. +HTTPX requires Python 3.8+. ## Documentation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/README_chinese.md new/httpx-0.25.0/README_chinese.md --- old/httpx-0.24.1/README_chinese.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/README_chinese.md 2023-09-11 12:13:24.000000000 +0200 @@ -101,7 +101,7 @@ $ pip install httpx[http2] ``` -HTTPX è¦æ± Python 3.7+ çæ¬ã +HTTPX è¦æ± Python 3.8+ çæ¬ã ## ææ¡£ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/advanced.md new/httpx-0.25.0/docs/advanced.md --- old/httpx-0.24.1/docs/advanced.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/advanced.md 2023-09-11 12:13:24.000000000 +0200 @@ -426,6 +426,38 @@  +## Monitoring upload progress + +If you need to monitor upload progress of large responses, you can use request content generator streaming. + +For example, showing a progress bar using the [`tqdm`](https://github.com/tqdm/tqdm) library. + +```python +import io +import random + +import httpx +from tqdm import tqdm + + +def gen(): + """ + this is a complete example with generated random bytes. + you can replace `io.BytesIO` with real file object. + """ + total = 32 * 1024 * 1024 # 32m + with tqdm(ascii=True, unit_scale=True, unit='B', unit_divisor=1024, total=total) as bar: + with io.BytesIO(random.randbytes(total)) as f: + while data := f.read(1024): + yield data + bar.update(len(data)) + + +httpx.post("https://httpbin.org/post", content=gen()) +``` + + + ## .netrc Support HTTPX can be configured to use [a `.netrc` config file](https://everything.curl.dev/usingcurl/netrc) for authentication. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/api.md new/httpx-0.25.0/docs/api.md --- old/httpx-0.24.1/docs/api.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/api.md 2023-09-11 12:13:24.000000000 +0200 @@ -70,7 +70,7 @@ * The amount of time elapsed between sending the request and calling `close()` on the corresponding response received for that request. [total_seconds()](https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds) to correctly get the total elapsed seconds. -* `def .raise_for_status()` - **None** +* `def .raise_for_status()` - **Response** * `def .json()` - **Any** * `def .read()` - **bytes** * `def .iter_raw([chunk_size])` - **bytes iterator** diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/async.md new/httpx-0.25.0/docs/async.md --- old/httpx-0.24.1/docs/async.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/async.md 2023-09-11 12:13:24.000000000 +0200 @@ -53,6 +53,9 @@ ... ``` +!!! warning +In order to get the most benefit from connection pooling, make sure you're not instantiating multiple client instances - for example by using `async with` inside a "hot loop". This can be achieved either by having a single scoped client that's passed throughout wherever it's needed, or by having a single global client instance. + Alternatively, use `await client.aclose()` if you want to close a client explicitly: ```python diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/index.md new/httpx-0.25.0/docs/index.md --- old/httpx-0.24.1/docs/index.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/index.md 2023-09-11 12:13:24.000000000 +0200 @@ -144,6 +144,6 @@ $ pip install httpx[brotli] ``` -HTTPX requires Python 3.7+ +HTTPX requires Python 3.8+ [sync-support]: https://github.com/encode/httpx/issues/572 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/logging.md new/httpx-0.25.0/docs/logging.md --- old/httpx-0.24.1/docs/logging.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/logging.md 2023-09-11 12:13:24.000000000 +0200 @@ -41,7 +41,7 @@ DEBUG [2023-03-16 14:36:21] httpcore - connection.close.complete ``` -Logging output includes information from both the high-level `httpx` logger, and the network-level `httpcore` logger, which can be configured seperately. +Logging output includes information from both the high-level `httpx` logger, and the network-level `httpcore` logger, which can be configured separately. For handling more complex logging configurations you might want to use the dictionary configuration style... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/quickstart.md new/httpx-0.25.0/docs/quickstart.md --- old/httpx-0.24.1/docs/quickstart.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/quickstart.md 2023-09-11 12:13:24.000000000 +0200 @@ -285,15 +285,22 @@ File "/Users/tomchristie/GitHub/encode/httpcore/httpx/models.py", line 837, in raise_for_status raise HTTPStatusError(message, response=self) httpx._exceptions.HTTPStatusError: 404 Client Error: Not Found for url: https://httpbin.org/status/404 -For more information check: https://httpstatuses.com/404 +For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404 ``` -Any successful response codes will simply return `None` rather than raising an exception. +Any successful response codes will return the `Response` instance rather than raising an exception. ```pycon >>> r.raise_for_status() ``` +The method returns the response instance, allowing you to use it inline. For example: + +```pycon +>>> r = httpx.get('...').raise_for_status() +>>> data = httpx.get('...').raise_for_status().json() +``` + ## Response Headers The response headers are available as a dictionary-like interface. @@ -367,7 +374,7 @@ ```pycon >>> with httpx.stream("GET", "https://www.example.com") as r: -... if r.headers['Content-Length'] < TOO_LONG: +... if int(r.headers['Content-Length']) < TOO_LONG: ... r.read() ... print(r.text) ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/docs/third_party_packages.md new/httpx-0.25.0/docs/third_party_packages.md --- old/httpx-0.24.1/docs/third_party_packages.md 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/docs/third_party_packages.md 2023-09-11 12:13:24.000000000 +0200 @@ -6,6 +6,12 @@ <!-- NOTE: this list is in alphabetical order. --> +### Hishel + +[GitHub](https://github.com/karosis88/hishel) - [Documentation](https://karosis88.github.io/hishel/) + +An elegant HTTP Cache implementation for HTTPX and HTTP Core. + ### Authlib [GitHub](https://github.com/lepture/authlib) - [Documentation](https://docs.authlib.org/en/latest/) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/__version__.py new/httpx-0.25.0/httpx/__version__.py --- old/httpx-0.24.1/httpx/__version__.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/__version__.py 2023-09-11 12:13:24.000000000 +0200 @@ -1,3 +1,3 @@ __title__ = "httpx" __description__ = "A next generation HTTP client, for Python 3." -__version__ = "0.24.1" +__version__ = "0.25.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_compat.py new/httpx-0.25.0/httpx/_compat.py --- old/httpx-0.24.1/httpx/_compat.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_compat.py 2023-09-11 12:13:24.000000000 +0200 @@ -17,7 +17,7 @@ brotli = None if sys.version_info >= (3, 10) or ( - sys.version_info >= (3, 7) and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0, 7) + sys.version_info >= (3, 8) and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0, 7) ): def set_minimum_tls_version_1_2(context: ssl.SSLContext) -> None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_config.py new/httpx-0.25.0/httpx/_config.py --- old/httpx-0.24.1/httpx/_config.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_config.py 2023-09-11 12:13:24.000000000 +0200 @@ -326,6 +326,7 @@ self, url: URLTypes, *, + ssl_context: typing.Optional[ssl.SSLContext] = None, auth: typing.Optional[typing.Tuple[str, str]] = None, headers: typing.Optional[HeaderTypes] = None, ): @@ -343,6 +344,7 @@ self.url = url self.auth = auth self.headers = headers + self.ssl_context = ssl_context @property def raw_auth(self) -> typing.Optional[typing.Tuple[bytes, bytes]]: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_models.py new/httpx-0.25.0/httpx/_models.py --- old/httpx-0.24.1/httpx/_models.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_models.py 2023-09-11 12:13:24.000000000 +0200 @@ -467,7 +467,7 @@ # the client will set `response.next_request`. self.next_request: typing.Optional[Request] = None - self.extensions = {} if extensions is None else extensions + self.extensions: ResponseExtensions = {} if extensions is None else extensions self.history = [] if history is None else list(history) self.is_closed = False @@ -711,7 +711,7 @@ and "Location" in self.headers ) - def raise_for_status(self) -> None: + def raise_for_status(self) -> "Response": """ Raise the `HTTPStatusError` if one occurred. """ @@ -723,18 +723,18 @@ ) if self.is_success: - return + return self if self.has_redirect_location: message = ( "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" "Redirect location: '{0.headers[location]}'\n" - "For more information check: https://httpstatuses.com/{0.status_code}" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" ) else: message = ( "{error_type} '{0.status_code} {0.reason_phrase}' for url '{0.url}'\n" - "For more information check: https://httpstatuses.com/{0.status_code}" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/{0.status_code}" ) status_class = self.status_code // 100 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_transports/asgi.py new/httpx-0.25.0/httpx/_transports/asgi.py --- old/httpx-0.24.1/httpx/_transports/asgi.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_transports/asgi.py 2023-09-11 12:13:24.000000000 +0200 @@ -161,9 +161,15 @@ try: await self.app(scope, receive, send) except Exception: # noqa: PIE-786 - if self.raise_app_exceptions or not response_complete.is_set(): + if self.raise_app_exceptions: raise + response_complete.set() + if status_code is None: + status_code = 500 + if response_headers is None: + response_headers = {} + assert response_complete.is_set() assert status_code is not None assert response_headers is not None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_transports/default.py new/httpx-0.25.0/httpx/_transports/default.py --- old/httpx-0.24.1/httpx/_transports/default.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_transports/default.py 2023-09-11 12:13:24.000000000 +0200 @@ -53,6 +53,12 @@ T = typing.TypeVar("T", bound="HTTPTransport") A = typing.TypeVar("A", bound="AsyncHTTPTransport") +SOCKET_OPTION = typing.Union[ + typing.Tuple[int, int, int], + typing.Tuple[int, int, typing.Union[bytes, bytearray]], + typing.Tuple[int, int, None, int], +] + @contextlib.contextmanager def map_httpcore_exceptions() -> typing.Iterator[None]: @@ -122,6 +128,7 @@ uds: typing.Optional[str] = None, local_address: typing.Optional[str] = None, retries: int = 0, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, ) -> None: ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) @@ -136,6 +143,7 @@ uds=uds, local_address=local_address, retries=retries, + socket_options=socket_options, ) elif proxy.url.scheme in ("http", "https"): self._pool = httpcore.HTTPProxy( @@ -148,11 +156,13 @@ proxy_auth=proxy.raw_auth, proxy_headers=proxy.headers.raw, ssl_context=ssl_context, + proxy_ssl_context=proxy.ssl_context, max_connections=limits.max_connections, max_keepalive_connections=limits.max_keepalive_connections, keepalive_expiry=limits.keepalive_expiry, http1=http1, http2=http2, + socket_options=socket_options, ) elif proxy.url.scheme == "socks5": try: @@ -257,6 +267,7 @@ uds: typing.Optional[str] = None, local_address: typing.Optional[str] = None, retries: int = 0, + socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None, ) -> None: ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env) @@ -271,6 +282,7 @@ uds=uds, local_address=local_address, retries=retries, + socket_options=socket_options, ) elif proxy.url.scheme in ("http", "https"): self._pool = httpcore.AsyncHTTPProxy( @@ -288,6 +300,7 @@ keepalive_expiry=limits.keepalive_expiry, http1=http1, http2=http2, + socket_options=socket_options, ) elif proxy.url.scheme == "socks5": try: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_transports/wsgi.py new/httpx-0.25.0/httpx/_transports/wsgi.py --- old/httpx-0.24.1/httpx/_transports/wsgi.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_transports/wsgi.py 2023-09-11 12:13:24.000000000 +0200 @@ -102,6 +102,7 @@ "QUERY_STRING": request.url.query.decode("ascii"), "SERVER_NAME": request.url.host, "SERVER_PORT": str(port), + "SERVER_PROTOCOL": "HTTP/1.1", "REMOTE_ADDR": self.remote_addr, } for header_key, header_value in request.headers.raw: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_types.py new/httpx-0.25.0/httpx/_types.py --- old/httpx-0.24.1/httpx/_types.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_types.py 2023-09-11 12:13:24.000000000 +0200 @@ -16,6 +16,7 @@ Iterator, List, Mapping, + MutableMapping, NamedTuple, Optional, Sequence, @@ -87,7 +88,7 @@ RequestContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] ResponseContent = Union[str, bytes, Iterable[bytes], AsyncIterable[bytes]] -ResponseExtensions = Mapping[str, Any] +ResponseExtensions = MutableMapping[str, Any] RequestData = Mapping[str, Any] @@ -104,7 +105,7 @@ ] RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] -RequestExtensions = Mapping[str, Any] +RequestExtensions = MutableMapping[str, Any] class SyncByteStream: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_urlparse.py new/httpx-0.25.0/httpx/_urlparse.py --- old/httpx-0.24.1/httpx/_urlparse.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_urlparse.py 2023-09-11 12:13:24.000000000 +0200 @@ -260,8 +260,10 @@ # For 'path' we need to drop ? and # from the GEN_DELIMS set. parsed_path: str = quote(path, safe=SUB_DELIMS + ":/[]@") # 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 + ":/?[]@") + 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] = ( @@ -452,11 +454,11 @@ # # https://github.com/python/cpython/blob/b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb/Lib/urllib/parse.py#L926 # - # Note that we use '%20' encoding for spaces, and treat '/' as a safe - # character. This means our query params have the same escaping as other - # characters in the URL path. This is slightly different to `requests`, - # but is the behaviour that browsers use. + # 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 and - # https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode - return "&".join([quote(k) + "=" + quote(v) for k, v in items]) + # 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]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/httpx/_utils.py new/httpx-0.25.0/httpx/_utils.py --- old/httpx-0.24.1/httpx/_utils.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/httpx/_utils.py 2023-09-11 12:13:24.000000000 +0200 @@ -357,12 +357,12 @@ A utility class currently used for making lookups against proxy keys... # Wildcard matching... - >>> pattern = URLPattern("all") + >>> pattern = URLPattern("all://") >>> pattern.matches(httpx.URL("http://example.com")) True # Witch scheme matching... - >>> pattern = URLPattern("https") + >>> pattern = URLPattern("https://") >>> pattern.matches(httpx.URL("https://example.com")) True >>> pattern.matches(httpx.URL("http://example.com")) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/pyproject.toml new/httpx-0.25.0/pyproject.toml --- old/httpx-0.24.1/pyproject.toml 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/pyproject.toml 2023-09-11 12:13:24.000000000 +0200 @@ -6,7 +6,7 @@ name = "httpx" description = "The next generation HTTP client." license = "BSD-3-Clause" -requires-python = ">=3.7" +requires-python = ">=3.8" authors = [ { name = "Tom Christie", email = "t...@tomchristie.com" }, ] @@ -20,7 +20,6 @@ "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -29,7 +28,7 @@ ] dependencies = [ "certifi", - "httpcore>=0.15.0,<0.18.0", + "httpcore>=0.18.0,<0.19.0", "idna", "sniffio", ] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/requirements.txt new/httpx-0.25.0/requirements.txt --- old/httpx-0.24.1/requirements.txt 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/requirements.txt 2023-09-11 12:13:24.000000000 +0200 @@ -6,27 +6,27 @@ # Optional charset auto-detection # Used in our test cases -chardet==5.1.0 +chardet==5.2.0 types-chardet==5.0.4.5 # Documentation -mkdocs==1.4.2 +mkdocs==1.5.2 mkautodoc==0.2.0 -mkdocs-material==9.1.5 +mkdocs-material==9.2.6 # Packaging build==0.10.0 twine==4.0.2 # Tests & Linting -black==23.3.0 -coverage[toml]==7.2.2 -cryptography==40.0.2 -mypy==1.0.1 +black==23.7.0 +coverage[toml]==7.3.0 +cryptography==41.0.3 +mypy==1.5.1 types-certifi==2021.10.8.2 -pytest==7.2.2 -ruff==0.0.260 -trio==0.22.0 +pytest==7.4.0 +ruff==0.0.286 +trio==0.22.2 trio-typing==0.8.0 -trustme==1.0.0 +trustme==1.1.0 uvicorn==0.22.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/scripts/check new/httpx-0.25.0/scripts/check --- old/httpx-0.24.1/scripts/check 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/scripts/check 2023-09-11 12:13:24.000000000 +0200 @@ -9,6 +9,6 @@ set -x ./scripts/sync-version -${PREFIX}black --check --diff --target-version=py37 $SOURCE_FILES +${PREFIX}black --check --diff $SOURCE_FILES ${PREFIX}mypy $SOURCE_FILES ${PREFIX}ruff check $SOURCE_FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/scripts/lint new/httpx-0.25.0/scripts/lint --- old/httpx-0.24.1/scripts/lint 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/scripts/lint 2023-09-11 12:13:24.000000000 +0200 @@ -1,7 +1,7 @@ #!/bin/sh -e export PREFIX="" -if [ -d 'venv' ] ; then +if [ -d 'venv' ]; then export PREFIX="venv/bin/" fi export SOURCE_FILES="httpx tests" @@ -9,4 +9,4 @@ set -x ${PREFIX}ruff --fix $SOURCE_FILES -${PREFIX}black --target-version=py37 $SOURCE_FILES +${PREFIX}black $SOURCE_FILES diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/client/test_async_client.py new/httpx-0.25.0/tests/client/test_async_client.py --- old/httpx-0.24.1/tests/client/test_async_client.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/client/test_async_client.py 2023-09-11 12:13:24.000000000 +0200 @@ -122,7 +122,7 @@ response.raise_for_status() assert exc_info.value.response == response else: - assert response.raise_for_status() is None # type: ignore + assert response.raise_for_status() is response @pytest.mark.anyio diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/client/test_client.py new/httpx-0.25.0/tests/client/test_client.py --- old/httpx-0.24.1/tests/client/test_client.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/client/test_client.py 2023-09-11 12:13:24.000000000 +0200 @@ -141,7 +141,7 @@ assert exc_info.value.response == response assert exc_info.value.request.url.path == f"/status/{status_code}" else: - assert response.raise_for_status() is None # type: ignore + assert response.raise_for_status() is response def test_options(server): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/models/test_responses.py new/httpx-0.25.0/tests/models/test_responses.py --- old/httpx-0.24.1/tests/models/test_responses.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/models/test_responses.py 2023-09-11 12:13:24.000000000 +0200 @@ -102,7 +102,7 @@ response.raise_for_status() assert str(exc_info.value) == ( "Informational response '101 Switching Protocols' for url 'https://example.org'\n" - "For more information check: https://httpstatuses.com/101" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/101" ) # 3xx status codes are redirections. @@ -114,7 +114,7 @@ assert str(exc_info.value) == ( "Redirect response '303 See Other' for url 'https://example.org'\n" "Redirect location: 'https://other.org'\n" - "For more information check: https://httpstatuses.com/303" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303" ) # 4xx status codes are a client error. @@ -125,7 +125,7 @@ response.raise_for_status() assert str(exc_info.value) == ( "Client error '403 Forbidden' for url 'https://example.org'\n" - "For more information check: https://httpstatuses.com/403" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403" ) # 5xx status codes are a server error. @@ -136,7 +136,7 @@ response.raise_for_status() assert str(exc_info.value) == ( "Server error '500 Internal Server Error' for url 'https://example.org'\n" - "For more information check: https://httpstatuses.com/500" + "For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500" ) # Calling .raise_for_status without setting a request instance is diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/models/test_url.py new/httpx-0.25.0/tests/models/test_url.py --- old/httpx-0.24.1/tests/models/test_url.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/models/test_url.py 2023-09-11 12:13:24.000000000 +0200 @@ -360,10 +360,10 @@ and https://github.com/encode/httpx/discussions/2460 """ url = httpx.URL("https://www.example.com/?a=b c&d=e/f") - assert url.raw_path == b"/?a=b%20c&d=e/f" + assert url.raw_path == b"/?a=b%20c&d=e%2Ff" url = httpx.URL("https://www.example.com/", params={"a": "b c", "d": "e/f"}) - assert url.raw_path == b"/?a=b%20c&d=e/f" + assert url.raw_path == b"/?a=b%20c&d=e%2Ff" def test_url_with_url_encoded_path(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/test_asgi.py new/httpx-0.25.0/tests/test_asgi.py --- old/httpx-0.24.1/tests/test_asgi.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/test_asgi.py 2023-09-11 12:13:24.000000000 +0200 @@ -3,6 +3,7 @@ import pytest import httpx +from httpx import ASGITransport async def hello_world(scope, receive, send): @@ -191,3 +192,12 @@ assert response.status_code == 200 assert disconnect + + +@pytest.mark.anyio +async def test_asgi_exc_no_raise(): + transport = ASGITransport(app=raise_exc, raise_app_exceptions=False) + async with httpx.AsyncClient(transport=transport) as client: + response = await client.get("http://www.example.org/") + + assert response.status_code == 500 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/test_config.py new/httpx-0.25.0/tests/test_config.py --- old/httpx-0.24.1/tests/test_config.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/test_config.py 2023-09-11 12:13:24.000000000 +0200 @@ -1,6 +1,5 @@ import os import ssl -import sys from pathlib import Path import certifi @@ -176,28 +175,26 @@ not hasattr(ssl.SSLContext, "keylog_filename"), reason="requires OpenSSL 1.1.1 or higher", ) -@pytest.mark.skipif(sys.version_info < (3, 8), reason="requires python3.8 or higher") def test_ssl_config_support_for_keylog_file(tmpdir, monkeypatch): # pragma: no cover - if sys.version_info > (3, 8): - with monkeypatch.context() as m: - m.delenv("SSLKEYLOGFILE", raising=False) + with monkeypatch.context() as m: + m.delenv("SSLKEYLOGFILE", raising=False) - context = httpx.create_ssl_context(trust_env=True) + context = httpx.create_ssl_context(trust_env=True) - assert context.keylog_filename is None + assert context.keylog_filename is None - filename = str(tmpdir.join("test.log")) + filename = str(tmpdir.join("test.log")) - with monkeypatch.context() as m: - m.setenv("SSLKEYLOGFILE", filename) + with monkeypatch.context() as m: + m.setenv("SSLKEYLOGFILE", filename) - context = httpx.create_ssl_context(trust_env=True) + context = httpx.create_ssl_context(trust_env=True) - assert context.keylog_filename == filename + assert context.keylog_filename == filename - context = httpx.create_ssl_context(trust_env=False) + context = httpx.create_ssl_context(trust_env=False) - assert context.keylog_filename is None + assert context.keylog_filename is None def test_proxy_from_url(): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/test_urlparse.py new/httpx-0.25.0/tests/test_urlparse.py --- old/httpx-0.24.1/tests/test_urlparse.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/test_urlparse.py 2023-09-11 12:13:24.000000000 +0200 @@ -141,7 +141,7 @@ def test_param_with_existing_escape_requires_encoding(): url = httpx.URL("http://webservice", params={"u": "http://example.com?q=foo%2Fa"}) - assert str(url) == "http://webservice?u=http%3A//example.com%3Fq%3Dfoo%252Fa" + assert str(url) == "http://webservice?u=http%3A%2F%2Fexample.com%3Fq%3Dfoo%252Fa" # Tests for invalid URLs @@ -264,9 +264,9 @@ def test_query_percent_encoding(): # Test percent encoding for SUB_DELIMS ALPHA NUM and allowable GEN_DELIMS url = httpx.URL("https://example.com/?!$&'()*+,;= abc ABC 123 :/[]@" + "?") - assert url.raw_path == b"/?!$&'()*+,;=%20abc%20ABC%20123%20:/[]@?" + assert url.raw_path == b"/?!$&'()*+,;=%20abc%20ABC%20123%20:%2F[]@?" assert url.path == "/" - assert url.query == b"!$&'()*+,;=%20abc%20ABC%20123%20:/[]@?" + assert url.query == b"!$&'()*+,;=%20abc%20ABC%20123%20:%2F[]@?" assert url.fragment == "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/httpx-0.24.1/tests/test_wsgi.py new/httpx-0.25.0/tests/test_wsgi.py --- old/httpx-0.24.1/tests/test_wsgi.py 2023-05-18 13:03:21.000000000 +0200 +++ new/httpx-0.25.0/tests/test_wsgi.py 2023-09-11 12:13:24.000000000 +0200 @@ -173,3 +173,20 @@ assert response.status_code == 200 assert response.text == "Hello, World!" assert server_port == expected_server_port + + +def test_wsgi_server_protocol(): + server_protocol = None + + def app(environ, start_response): + nonlocal server_protocol + server_protocol = environ["SERVER_PROTOCOL"] + start_response("200 OK", [("Content-Type", "text/plain")]) + return [b"success"] + + with httpx.Client(app=app, base_url="http://testserver") as client: + response = client.get("/") + + assert response.status_code == 200 + assert response.text == "success" + assert server_protocol == "HTTP/1.1"