summaryrefslogtreecommitdiffstats
path: root/contrib/python/httpcore
diff options
context:
space:
mode:
authorAlexSm <[email protected]>2024-01-04 15:09:05 +0100
committerGitHub <[email protected]>2024-01-04 15:09:05 +0100
commitdab291146f6cd7d35684e3a1150e5bb1c412982c (patch)
tree36ef35f6cacb6432845a4a33f940c95871036b32 /contrib/python/httpcore
parent63660ad5e7512029fd0218e7a636580695a24e1f (diff)
Library import 5, delete go dependencies (#832)
* Library import 5, delete go dependencies * Fix yt client
Diffstat (limited to 'contrib/python/httpcore')
-rw-r--r--contrib/python/httpcore/.dist-info/METADATA66
-rw-r--r--contrib/python/httpcore/README.md36
-rw-r--r--contrib/python/httpcore/httpcore/__init__.py2
-rw-r--r--contrib/python/httpcore/httpcore/_async/connection_pool.py20
-rw-r--r--contrib/python/httpcore/httpcore/_backends/auto.py5
-rw-r--r--contrib/python/httpcore/httpcore/_backends/sync.py6
-rw-r--r--contrib/python/httpcore/httpcore/_sync/connection_pool.py20
-rw-r--r--contrib/python/httpcore/httpcore/_synchronization.py108
-rw-r--r--contrib/python/httpcore/ya.make4
9 files changed, 165 insertions, 102 deletions
diff --git a/contrib/python/httpcore/.dist-info/METADATA b/contrib/python/httpcore/.dist-info/METADATA
index 3776738cafc..07eab9de210 100644
--- a/contrib/python/httpcore/.dist-info/METADATA
+++ b/contrib/python/httpcore/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: httpcore
-Version: 0.18.0
+Version: 1.0.2
Summary: A minimal low-level HTTP client.
Project-URL: Documentation, https://www.encode.io/httpcore
Project-URL: Homepage, https://www.encode.io/httpcore/
@@ -21,16 +21,19 @@ Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Python: >=3.8
-Requires-Dist: anyio<5.0,>=3.0
Requires-Dist: certifi
Requires-Dist: h11<0.15,>=0.13
-Requires-Dist: sniffio==1.*
+Provides-Extra: asyncio
+Requires-Dist: anyio<5.0,>=4.0; extra == 'asyncio'
Provides-Extra: http2
Requires-Dist: h2<5,>=3; extra == 'http2'
Provides-Extra: socks
Requires-Dist: socksio==1.*; extra == 'socks'
+Provides-Extra: trio
+Requires-Dist: trio<0.23.0,>=0.22.0; extra == 'trio'
Description-Content-Type: text/markdown
# HTTP Core
@@ -70,16 +73,10 @@ For HTTP/1.1 only support, install with:
$ pip install httpcore
```
-For HTTP/1.1 and HTTP/2 support, install with:
+There are also a number of optional extras available...
```shell
-$ pip install httpcore[http2]
-```
-
-For SOCKS proxy support, install with:
-
-```shell
-$ pip install httpcore[socks]
+$ pip install httpcore['asyncio,trio,http2,socks']
```
# Sending requests
@@ -124,12 +121,59 @@ The motivation for `httpcore` is:
* To provide a reusable low-level client library, that other packages can then build on top of.
* To provide a *really clear interface split* between the networking code and client logic,
so that each is easier to understand and reason about in isolation.
+
+## Dependencies
+
+The `httpcore` package has the following dependencies...
+
+* `h11`
+* `certifi`
+
+And the following optional extras...
+
+* `anyio` - Required by `pip install httpcore['asyncio']`.
+* `trio` - Required by `pip install httpcore['trio']`.
+* `h2` - Required by `pip install httpcore['http2']`.
+* `socksio` - Required by `pip install httpcore['socks']`.
+
+## Versioning
+
+We use [SEMVER for our versioning policy](https://semver.org/).
+
+For changes between package versions please see our [project changelog](CHANGELOG.md).
+
+We recommend pinning your requirements either the most current major version, or a more specific version range:
+
+```python
+pip install 'httpcore==1.*'
+```
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+## 1.0.2 (November 10th, 2023)
+
+- Fix `float("inf")` timeouts in `Event.wait` function. (#846)
+
+## 1.0.1 (November 3rd, 2023)
+
+- Fix pool timeout to account for the total time spent retrying. (#823)
+- Raise a neater RuntimeError when the correct async deps are not installed. (#826)
+- Add support for synchronous TLS-in-TLS streams. (#840)
+
+## 1.0.0 (October 6th, 2023)
+
+From version 1.0 our async support is now optional, as the package has minimal dependencies by default.
+
+For async support use either `pip install 'httpcore[asyncio]'` or `pip install 'httpcore[trio]'`.
+
+The project versioning policy is now explicitly governed by SEMVER. See https://semver.org/.
+
+- Async support becomes fully optional. (#809)
+- Add support for Python 3.12. (#807)
+
## 0.18.0 (September 8th, 2023)
- Add support for HTTPS proxies. (#745, #786)
diff --git a/contrib/python/httpcore/README.md b/contrib/python/httpcore/README.md
index 66a21500169..e7bfd5838f6 100644
--- a/contrib/python/httpcore/README.md
+++ b/contrib/python/httpcore/README.md
@@ -35,16 +35,10 @@ For HTTP/1.1 only support, install with:
$ pip install httpcore
```
-For HTTP/1.1 and HTTP/2 support, install with:
+There are also a number of optional extras available...
```shell
-$ pip install httpcore[http2]
-```
-
-For SOCKS proxy support, install with:
-
-```shell
-$ pip install httpcore[socks]
+$ pip install httpcore['asyncio,trio,http2,socks']
```
# Sending requests
@@ -89,3 +83,29 @@ The motivation for `httpcore` is:
* To provide a reusable low-level client library, that other packages can then build on top of.
* To provide a *really clear interface split* between the networking code and client logic,
so that each is easier to understand and reason about in isolation.
+
+## Dependencies
+
+The `httpcore` package has the following dependencies...
+
+* `h11`
+* `certifi`
+
+And the following optional extras...
+
+* `anyio` - Required by `pip install httpcore['asyncio']`.
+* `trio` - Required by `pip install httpcore['trio']`.
+* `h2` - Required by `pip install httpcore['http2']`.
+* `socksio` - Required by `pip install httpcore['socks']`.
+
+## Versioning
+
+We use [SEMVER for our versioning policy](https://semver.org/).
+
+For changes between package versions please see our [project changelog](CHANGELOG.md).
+
+We recommend pinning your requirements either the most current major version, or a more specific version range:
+
+```python
+pip install 'httpcore==1.*'
+```
diff --git a/contrib/python/httpcore/httpcore/__init__.py b/contrib/python/httpcore/httpcore/__init__.py
index 65abe9716a1..eb3e577186b 100644
--- a/contrib/python/httpcore/httpcore/__init__.py
+++ b/contrib/python/httpcore/httpcore/__init__.py
@@ -130,7 +130,7 @@ __all__ = [
"WriteError",
]
-__version__ = "0.18.0"
+__version__ = "1.0.2"
__locals = locals()
diff --git a/contrib/python/httpcore/httpcore/_async/connection_pool.py b/contrib/python/httpcore/httpcore/_async/connection_pool.py
index ddc0510e60e..0320c6d80e4 100644
--- a/contrib/python/httpcore/httpcore/_async/connection_pool.py
+++ b/contrib/python/httpcore/httpcore/_async/connection_pool.py
@@ -1,11 +1,12 @@
import ssl
import sys
+import time
from types import TracebackType
from typing import AsyncIterable, AsyncIterator, Iterable, List, Optional, Type
from .._backends.auto import AutoBackend
from .._backends.base import SOCKET_OPTION, AsyncNetworkBackend
-from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol
+from .._exceptions import ConnectionNotAvailable, PoolTimeout, UnsupportedProtocol
from .._models import Origin, Request, Response
from .._synchronization import AsyncEvent, AsyncLock, AsyncShieldCancellation
from .connection import AsyncHTTPConnection
@@ -220,6 +221,13 @@ class AsyncConnectionPool(AsyncRequestInterface):
)
status = RequestStatus(request)
+ timeouts = request.extensions.get("timeout", {})
+ timeout = timeouts.get("pool", None)
+
+ if timeout is not None:
+ deadline = time.monotonic() + timeout
+ else:
+ deadline = float("inf")
async with self._pool_lock:
self._requests.append(status)
@@ -227,8 +235,6 @@ class AsyncConnectionPool(AsyncRequestInterface):
await self._attempt_to_acquire_connection(status)
while True:
- timeouts = request.extensions.get("timeout", {})
- timeout = timeouts.get("pool", None)
try:
connection = await status.wait_for_connection(timeout=timeout)
except BaseException as exc:
@@ -263,6 +269,10 @@ class AsyncConnectionPool(AsyncRequestInterface):
else:
break
+ timeout = deadline - time.monotonic()
+ if timeout < 0:
+ raise PoolTimeout # pragma: nocover
+
# When we return the response, we wrap the stream in a special class
# that handles notifying the connection pool once the response
# has been released.
@@ -316,6 +326,10 @@ class AsyncConnectionPool(AsyncRequestInterface):
self._requests = []
async def __aenter__(self) -> "AsyncConnectionPool":
+ # Acquiring the pool lock here ensures that we have the
+ # correct dependencies installed as early as possible.
+ async with self._pool_lock:
+ pass
return self
async def __aexit__(
diff --git a/contrib/python/httpcore/httpcore/_backends/auto.py b/contrib/python/httpcore/httpcore/_backends/auto.py
index b612ba071ca..3ac05f4da0b 100644
--- a/contrib/python/httpcore/httpcore/_backends/auto.py
+++ b/contrib/python/httpcore/httpcore/_backends/auto.py
@@ -1,15 +1,14 @@
import typing
from typing import Optional
-import sniffio
-
+from .._synchronization import current_async_library
from .base import SOCKET_OPTION, AsyncNetworkBackend, AsyncNetworkStream
class AutoBackend(AsyncNetworkBackend):
async def _init_backend(self) -> None:
if not (hasattr(self, "_backend")):
- backend = sniffio.current_async_library()
+ backend = current_async_library()
if backend == "trio":
from .trio import TrioBackend
diff --git a/contrib/python/httpcore/httpcore/_backends/sync.py b/contrib/python/httpcore/httpcore/_backends/sync.py
index f2dbd32afa4..7b7b417dc19 100644
--- a/contrib/python/httpcore/httpcore/_backends/sync.py
+++ b/contrib/python/httpcore/httpcore/_backends/sync.py
@@ -145,12 +145,6 @@ class SyncStream(NetworkStream):
server_hostname: typing.Optional[str] = None,
timeout: typing.Optional[float] = None,
) -> NetworkStream:
- if isinstance(self._sock, ssl.SSLSocket): # pragma: no cover
- raise RuntimeError(
- "Attempted to add a TLS layer on top of the existing "
- "TLS stream, which is not supported by httpcore package"
- )
-
exc_map: ExceptionMapping = {
socket.timeout: ConnectTimeout,
OSError: ConnectError,
diff --git a/contrib/python/httpcore/httpcore/_sync/connection_pool.py b/contrib/python/httpcore/httpcore/_sync/connection_pool.py
index dbcaff1fcf1..ccfb8d22208 100644
--- a/contrib/python/httpcore/httpcore/_sync/connection_pool.py
+++ b/contrib/python/httpcore/httpcore/_sync/connection_pool.py
@@ -1,11 +1,12 @@
import ssl
import sys
+import time
from types import TracebackType
from typing import Iterable, Iterator, Iterable, List, Optional, Type
from .._backends.sync import SyncBackend
from .._backends.base import SOCKET_OPTION, NetworkBackend
-from .._exceptions import ConnectionNotAvailable, UnsupportedProtocol
+from .._exceptions import ConnectionNotAvailable, PoolTimeout, UnsupportedProtocol
from .._models import Origin, Request, Response
from .._synchronization import Event, Lock, ShieldCancellation
from .connection import HTTPConnection
@@ -220,6 +221,13 @@ class ConnectionPool(RequestInterface):
)
status = RequestStatus(request)
+ timeouts = request.extensions.get("timeout", {})
+ timeout = timeouts.get("pool", None)
+
+ if timeout is not None:
+ deadline = time.monotonic() + timeout
+ else:
+ deadline = float("inf")
with self._pool_lock:
self._requests.append(status)
@@ -227,8 +235,6 @@ class ConnectionPool(RequestInterface):
self._attempt_to_acquire_connection(status)
while True:
- timeouts = request.extensions.get("timeout", {})
- timeout = timeouts.get("pool", None)
try:
connection = status.wait_for_connection(timeout=timeout)
except BaseException as exc:
@@ -263,6 +269,10 @@ class ConnectionPool(RequestInterface):
else:
break
+ timeout = deadline - time.monotonic()
+ if timeout < 0:
+ raise PoolTimeout # pragma: nocover
+
# When we return the response, we wrap the stream in a special class
# that handles notifying the connection pool once the response
# has been released.
@@ -316,6 +326,10 @@ class ConnectionPool(RequestInterface):
self._requests = []
def __enter__(self) -> "ConnectionPool":
+ # Acquiring the pool lock here ensures that we have the
+ # correct dependencies installed as early as possible.
+ with self._pool_lock:
+ pass
return self
def __exit__(
diff --git a/contrib/python/httpcore/httpcore/_synchronization.py b/contrib/python/httpcore/httpcore/_synchronization.py
index bae27c1b112..119d89fc0d5 100644
--- a/contrib/python/httpcore/httpcore/_synchronization.py
+++ b/contrib/python/httpcore/httpcore/_synchronization.py
@@ -2,8 +2,6 @@ import threading
from types import TracebackType
from typing import Optional, Type
-import sniffio
-
from ._exceptions import ExceptionMapping, PoolTimeout, map_exceptions
# Our async synchronization primatives use either 'anyio' or 'trio' depending
@@ -20,6 +18,32 @@ except ImportError: # pragma: nocover
anyio = None # type: ignore
+def current_async_library() -> str:
+ # Determine if we're running under trio or asyncio.
+ # See https://sniffio.readthedocs.io/en/latest/
+ try:
+ import sniffio
+ except ImportError: # pragma: nocover
+ environment = "asyncio"
+ else:
+ environment = sniffio.current_async_library()
+
+ if environment not in ("asyncio", "trio"): # pragma: nocover
+ raise RuntimeError("Running under an unsupported async environment.")
+
+ if environment == "asyncio" and anyio is None: # pragma: nocover
+ raise RuntimeError(
+ "Running with asyncio requires installation of 'httpcore[asyncio]'."
+ )
+
+ if environment == "trio" and trio is None: # pragma: nocover
+ raise RuntimeError(
+ "Running with trio requires installation of 'httpcore[trio]'."
+ )
+
+ return environment
+
+
class AsyncLock:
def __init__(self) -> None:
self._backend = ""
@@ -29,18 +53,10 @@ class AsyncLock:
Detect if we're running under 'asyncio' or 'trio' and create
a lock with the correct implementation.
"""
- self._backend = sniffio.current_async_library()
+ self._backend = current_async_library()
if self._backend == "trio":
- if trio is None: # pragma: nocover
- raise RuntimeError(
- "Running under trio, requires the 'trio' package to be installed."
- )
self._trio_lock = trio.Lock()
- else:
- if anyio is None: # pragma: nocover
- raise RuntimeError(
- "Running under asyncio requires the 'anyio' package to be installed."
- )
+ elif self._backend == "asyncio":
self._anyio_lock = anyio.Lock()
async def __aenter__(self) -> "AsyncLock":
@@ -49,7 +65,7 @@ class AsyncLock:
if self._backend == "trio":
await self._trio_lock.acquire()
- else:
+ elif self._backend == "asyncio":
await self._anyio_lock.acquire()
return self
@@ -62,7 +78,7 @@ class AsyncLock:
) -> None:
if self._backend == "trio":
self._trio_lock.release()
- else:
+ elif self._backend == "asyncio":
self._anyio_lock.release()
@@ -75,18 +91,10 @@ class AsyncEvent:
Detect if we're running under 'asyncio' or 'trio' and create
a lock with the correct implementation.
"""
- self._backend = sniffio.current_async_library()
+ self._backend = current_async_library()
if self._backend == "trio":
- if trio is None: # pragma: nocover
- raise RuntimeError(
- "Running under trio requires the 'trio' package to be installed."
- )
self._trio_event = trio.Event()
- else:
- if anyio is None: # pragma: nocover
- raise RuntimeError(
- "Running under asyncio requires the 'anyio' package to be installed."
- )
+ elif self._backend == "asyncio":
self._anyio_event = anyio.Event()
def set(self) -> None:
@@ -95,7 +103,7 @@ class AsyncEvent:
if self._backend == "trio":
self._trio_event.set()
- else:
+ elif self._backend == "asyncio":
self._anyio_event.set()
async def wait(self, timeout: Optional[float] = None) -> None:
@@ -103,22 +111,12 @@ class AsyncEvent:
self.setup()
if self._backend == "trio":
- if trio is None: # pragma: nocover
- raise RuntimeError(
- "Running under trio requires the 'trio' package to be installed."
- )
-
trio_exc_map: ExceptionMapping = {trio.TooSlowError: PoolTimeout}
timeout_or_inf = float("inf") if timeout is None else timeout
with map_exceptions(trio_exc_map):
with trio.fail_after(timeout_or_inf):
await self._trio_event.wait()
- else:
- if anyio is None: # pragma: nocover
- raise RuntimeError(
- "Running under asyncio requires the 'anyio' package to be installed."
- )
-
+ elif self._backend == "asyncio":
anyio_exc_map: ExceptionMapping = {TimeoutError: PoolTimeout}
with map_exceptions(anyio_exc_map):
with anyio.fail_after(timeout):
@@ -135,22 +133,12 @@ class AsyncSemaphore:
Detect if we're running under 'asyncio' or 'trio' and create
a semaphore with the correct implementation.
"""
- self._backend = sniffio.current_async_library()
+ self._backend = current_async_library()
if self._backend == "trio":
- if trio is None: # pragma: nocover
- raise RuntimeError(
- "Running under trio requires the 'trio' package to be installed."
- )
-
self._trio_semaphore = trio.Semaphore(
initial_value=self._bound, max_value=self._bound
)
- else:
- if anyio is None: # pragma: nocover
- raise RuntimeError(
- "Running under asyncio requires the 'anyio' package to be installed."
- )
-
+ elif self._backend == "asyncio":
self._anyio_semaphore = anyio.Semaphore(
initial_value=self._bound, max_value=self._bound
)
@@ -161,13 +149,13 @@ class AsyncSemaphore:
if self._backend == "trio":
await self._trio_semaphore.acquire()
- else:
+ elif self._backend == "asyncio":
await self._anyio_semaphore.acquire()
async def release(self) -> None:
if self._backend == "trio":
self._trio_semaphore.release()
- else:
+ elif self._backend == "asyncio":
self._anyio_semaphore.release()
@@ -184,27 +172,17 @@ class AsyncShieldCancellation:
Detect if we're running under 'asyncio' or 'trio' and create
a shielded scope with the correct implementation.
"""
- self._backend = sniffio.current_async_library()
+ self._backend = current_async_library()
if self._backend == "trio":
- if trio is None: # pragma: nocover
- raise RuntimeError(
- "Running under trio requires the 'trio' package to be installed."
- )
-
self._trio_shield = trio.CancelScope(shield=True)
- else:
- if anyio is None: # pragma: nocover
- raise RuntimeError(
- "Running under asyncio requires the 'anyio' package to be installed."
- )
-
+ elif self._backend == "asyncio":
self._anyio_shield = anyio.CancelScope(shield=True)
def __enter__(self) -> "AsyncShieldCancellation":
if self._backend == "trio":
self._trio_shield.__enter__()
- else:
+ elif self._backend == "asyncio":
self._anyio_shield.__enter__()
return self
@@ -216,7 +194,7 @@ class AsyncShieldCancellation:
) -> None:
if self._backend == "trio":
self._trio_shield.__exit__(exc_type, exc_value, traceback)
- else:
+ elif self._backend == "asyncio":
self._anyio_shield.__exit__(exc_type, exc_value, traceback)
@@ -248,6 +226,8 @@ class Event:
self._event.set()
def wait(self, timeout: Optional[float] = None) -> None:
+ if timeout == float("inf"): # pragma: no cover
+ timeout = None
if not self._event.wait(timeout=timeout):
raise PoolTimeout() # pragma: nocover
diff --git a/contrib/python/httpcore/ya.make b/contrib/python/httpcore/ya.make
index e8516afe103..de7a3ac5a2f 100644
--- a/contrib/python/httpcore/ya.make
+++ b/contrib/python/httpcore/ya.make
@@ -2,15 +2,13 @@
PY3_LIBRARY()
-VERSION(0.18.0)
+VERSION(1.0.2)
LICENSE(BSD-3-Clause)
PEERDIR(
- contrib/python/anyio
contrib/python/certifi
contrib/python/h11
- contrib/python/sniffio
)
NO_LINT()