diff --git a/pyproject.toml b/pyproject.toml index c42387030..a5b4ff257 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,6 +86,7 @@ dev = [ "inline-snapshot>=0.28.0", "griffe>=1", "http-snapshot[httpx]==0.1.8", + "httpx2 ; python_version >= '3.10'", ] pydantic-v1 = [ "pydantic>=1.9.0,<2", @@ -159,6 +160,7 @@ exclude = [ ".venv", ".nox", "examples/mcp_tool_runner.py", # mcp requires Python 3.10+, lint runs on 3.9 + "tests/test_httpx2_client.py", # httpx2 requires Python 3.10+, lint runs on 3.9 ] reportImplicitOverride = true diff --git a/requirements-dev.lock b/requirements-dev.lock index 5cfa1c4ee..57bf4c05a 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -7,6 +7,7 @@ anyio==4.12.1 # via # anthropic # httpx + # httpx2 asttokens==3.0.1 # via inline-snapshot backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' @@ -45,19 +46,29 @@ griffelib==2.0.0 ; python_full_version >= '3.10' # griffe # griffecli h11==0.16.0 - # via httpcore + # via + # httpcore + # httpcore2 http-snapshot==0.1.8 httpcore==1.0.9 # via httpx +httpcore2==2.7.0 ; python_full_version >= '3.10' + # via httpx2 httpx==0.28.1 # via # anthropic # http-snapshot # respx -idna==3.11 +httpx2==2.7.0 ; python_full_version >= '3.10' +idna==3.11 ; python_full_version < '3.10' + # via + # anyio + # httpx +idna==3.18 ; python_full_version >= '3.10' # via # anyio # httpx + # httpx2 importlib-metadata==8.7.1 iniconfig==2.1.0 ; python_full_version < '3.10' # via pytest @@ -123,6 +134,10 @@ tomli==2.4.0 ; python_full_version < '3.11' # inline-snapshot # mypy # pytest +truststore==0.10.4 ; python_full_version >= '3.10' + # via + # httpcore2 + # httpx2 types-awscrt==0.31.3 # via botocore-stubs types-s3transfer==0.16.0 @@ -133,6 +148,7 @@ typing-extensions==4.15.0 # anyio # boto3-stubs # exceptiongroup + # httpx2 # inline-snapshot # mypy # pydantic diff --git a/src/anthropic/_base_client.py b/src/anthropic/_base_client.py index 98d154c09..6fbebb4e3 100644 --- a/src/anthropic/_base_client.py +++ b/src/anthropic/_base_client.py @@ -134,6 +134,20 @@ HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) +# httpx2 (https://github.com/pydantic/httpx2) is an API-compatible fork of httpx that +# lives in a separate import namespace, so `httpx2.Client` & co. are distinct classes +# that fail `isinstance(..., httpx.Client)`. Accept an injected httpx2 client when the +# package is installed. +try: + import httpx2 # type: ignore +except ImportError: + httpx2 = None + +_SYNC_HTTP_CLIENT_TYPES = (httpx.Client,) if httpx2 is None else (httpx.Client, httpx2.Client) +_ASYNC_HTTP_CLIENT_TYPES = (httpx.AsyncClient,) if httpx2 is None else (httpx.AsyncClient, httpx2.AsyncClient) +_TIMEOUT_EXCEPTIONS = (httpx.TimeoutException,) if httpx2 is None else (httpx.TimeoutException, httpx2.TimeoutException) + + class PageInfo: """Stores the necessary information to build the request to retrieve the next page. @@ -599,12 +613,20 @@ def _build_request( headers.pop("Content-Type", None) kwargs.pop("data", None) + # httpx2's `build_request` rejects a classic `httpx.URL`, so pass the URL as a + # string for httpx2 clients while leaving classic httpx clients untouched. + url = ( + str(prepared_url) + if httpx2 is not None and isinstance(self._client, (httpx2.Client, httpx2.AsyncClient)) + else prepared_url + ) + # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, method=options.method, - url=prepared_url, + url=url, # the `Query` type that we use is incompatible with qs' # `Params` type as it needs to be typed as `Mapping[str, object]` # so that passing a `TypedDict` doesn't cause an error. @@ -1007,7 +1029,7 @@ def __init__( else: timeout = DEFAULT_TIMEOUT - if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] + if http_client is not None and not isinstance(http_client, _SYNC_HTTP_CLIENT_TYPES): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError( f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" ) @@ -1297,7 +1319,7 @@ def _attempt_request( stream=stream or self._should_stream_response_body(request=request), **kwargs, ) - except httpx.TimeoutException as err: + except _TIMEOUT_EXCEPTIONS as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) raise APITimeoutError(request=request) from err except Exception as err: @@ -1749,7 +1771,7 @@ def __init__( else: timeout = DEFAULT_TIMEOUT - if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] + if http_client is not None and not isinstance(http_client, _ASYNC_HTTP_CLIENT_TYPES): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError( f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" ) @@ -2049,7 +2071,7 @@ async def _attempt_request( stream=stream or self._should_stream_response_body(request=request), **kwargs, ) - except httpx.TimeoutException as err: + except _TIMEOUT_EXCEPTIONS as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) raise APITimeoutError(request=request) from err except Exception as err: diff --git a/src/anthropic/_response.py b/src/anthropic/_response.py index 1e0257ee4..07cff807f 100644 --- a/src/anthropic/_response.py +++ b/src/anthropic/_response.py @@ -37,6 +37,19 @@ from ._base_client import BaseClient +# httpx2 (https://github.com/pydantic/httpx2) raises its own `StreamConsumed` from a +# distinct import namespace, so an injected httpx2 client's double-read would escape an +# `except httpx.StreamConsumed`. Catch both when the package is installed. +try: + import httpx2 # type: ignore +except ImportError: + httpx2 = None + +_STREAM_CONSUMED_EXCEPTIONS = ( + (httpx.StreamConsumed,) if httpx2 is None else (httpx.StreamConsumed, httpx2.StreamConsumed) +) + + P = ParamSpec("P") R = TypeVar("R") _T = TypeVar("_T") @@ -361,7 +374,7 @@ def read(self) -> bytes: """Read and return the binary response content.""" try: return self.http_response.read() - except httpx.StreamConsumed as exc: + except _STREAM_CONSUMED_EXCEPTIONS as exc: # The default error raised by httpx isn't very # helpful in our case so we re-raise it with # a different error message. @@ -468,7 +481,7 @@ async def read(self) -> bytes: """Read and return the binary response content.""" try: return await self.http_response.aread() - except httpx.StreamConsumed as exc: + except _STREAM_CONSUMED_EXCEPTIONS as exc: # the default error raised by httpx isn't very # helpful in our case so we re-raise it with # a different error message diff --git a/tests/test_httpx2_client.py b/tests/test_httpx2_client.py new file mode 100644 index 000000000..39f6e32c4 --- /dev/null +++ b/tests/test_httpx2_client.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +import httpx +import pytest + +from anthropic import Anthropic, AsyncAnthropic + +httpx2 = pytest.importorskip("httpx2") + +base_url = "http://127.0.0.1:4010" +api_key = "my-anthropic-api-key" + + +def test_accepts_httpx2_sync_client() -> None: + with httpx2.Client() as http_client: + client = Anthropic( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=http_client, + ) + assert isinstance(client._client, httpx2.Client) + + +async def test_accepts_httpx2_async_client() -> None: + async with httpx2.AsyncClient() as http_client: + client = AsyncAnthropic( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=http_client, + ) + assert isinstance(client._client, httpx2.AsyncClient) + + +def test_httpx2_sync_round_trip() -> None: + def handler(_request: httpx2.Request) -> httpx2.Response: + return httpx2.Response(200, json={"ok": True}) + + with httpx2.Client(transport=httpx2.MockTransport(handler)) as http_client: + with Anthropic( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=http_client, + ) as client: + response = client.get("/foo", cast_to=httpx.Response) + assert response.status_code == 200 + + +async def test_httpx2_async_round_trip() -> None: + def handler(_request: httpx2.Request) -> httpx2.Response: + return httpx2.Response(200, json={"ok": True}) + + async with httpx2.AsyncClient(transport=httpx2.MockTransport(handler)) as http_client: + async with AsyncAnthropic( + base_url=base_url, + api_key=api_key, + _strict_response_validation=True, + http_client=http_client, + ) as client: + response = await client.get("/foo", cast_to=httpx.Response) + assert response.status_code == 200 diff --git a/uv.lock b/uv.lock index d62347c3c..f1f0417c1 100644 --- a/uv.lock +++ b/uv.lock @@ -194,7 +194,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.102.0" +version = "0.117.0" source = { editable = "." } dependencies = [ { name = "anyio" }, @@ -221,6 +221,9 @@ bedrock = [ { name = "boto3" }, { name = "botocore" }, ] +google-cloud = [ + { name = "google-auth", extra = ["requests"] }, +] mcp = [ { name = "mcp", marker = "python_full_version >= '3.10'" }, ] @@ -238,6 +241,7 @@ dev = [ { name = "griffe", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "griffe", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "http-snapshot", extra = ["httpx"] }, + { name = "httpx2", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "importlib-metadata" }, { name = "inline-snapshot" }, { name = "mypy" }, @@ -270,6 +274,7 @@ requires-dist = [ { name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.31.57" }, { name = "distro", specifier = ">=1.7.0,<2" }, { name = "docstring-parser", specifier = ">=0.15,<1" }, + { name = "google-auth", extras = ["requests"], marker = "extra == 'google-cloud'", specifier = ">=2,<3" }, { name = "google-auth", extras = ["requests"], marker = "extra == 'vertex'", specifier = ">=2,<3" }, { name = "httpx", specifier = ">=0.25.0,<1" }, { name = "httpx-aiohttp", marker = "extra == 'aiohttp'", specifier = ">=0.1.9" }, @@ -280,7 +285,7 @@ requires-dist = [ { name = "standardwebhooks", marker = "extra == 'webhooks'", specifier = ">=1.0.1,<2" }, { name = "typing-extensions", specifier = ">=4.14,<5" }, ] -provides-extras = ["aiohttp", "vertex", "aws", "bedrock", "mcp", "webhooks"] +provides-extras = ["aiohttp", "vertex", "google-cloud", "aws", "bedrock", "mcp", "webhooks"] [package.metadata.requires-dev] dev = [ @@ -288,6 +293,7 @@ dev = [ { name = "dirty-equals", specifier = ">=0.6.0" }, { name = "griffe", specifier = ">=1" }, { name = "http-snapshot", extras = ["httpx"], specifier = "==0.1.8" }, + { name = "httpx2", marker = "python_full_version >= '3.10'" }, { name = "importlib-metadata", specifier = ">=6.7.0" }, { name = "inline-snapshot", specifier = ">=0.28.0" }, { name = "mypy", specifier = "==1.17" }, @@ -312,7 +318,8 @@ version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, - { name = "idna" }, + { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "idna", version = "3.18", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } @@ -1039,6 +1046,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, ] +[[package]] +name = "httpcore2" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h11", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "truststore", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/fe/6a3f9f1a8bb8733326140737446aaf72fddb8b54b8f202302f5c84960613/httpcore2-2.7.0.tar.gz", hash = "sha256:6dc0fedf329a52a990930a5579edfebaea81118ea700ea0dd7de2b5e5be49efc", size = 65593, upload-time = "2026-07-14T20:40:01.111Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/6c/62e2e279e63fc4f7a5ee841ef13175a8bbc613f258e9dcc186e9de803a42/httpcore2-2.7.0-py3-none-any.whl", hash = "sha256:1452f589fe23f55b44546cd884294c41a29330af902bc0b71a761fd52d18f92b", size = 81506, upload-time = "2026-07-14T20:39:58.053Z" }, +] + [[package]] name = "httpx" version = "0.28.1" @@ -1047,7 +1067,8 @@ dependencies = [ { name = "anyio" }, { name = "certifi" }, { name = "httpcore" }, - { name = "idna" }, + { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "idna", version = "3.18", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ @@ -1076,15 +1097,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] +[[package]] +name = "httpx2" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "httpcore2", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "idna", version = "3.18", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "truststore", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "typing-extensions", marker = "(python_full_version >= '3.10' and python_full_version < '3.13') or (python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version < '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2') or (python_full_version >= '3.13' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version >= '3.13' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/4a/129b2e21b90ac2985d3928d96792bccc39bc6dfe796c5eee2d8ec06d4105/httpx2-2.7.0.tar.gz", hash = "sha256:8b30709aed5c8465b0dd3b95c09ce301c8f79e7e7a2d00ab0af551e0d0375b07", size = 94487, upload-time = "2026-07-14T20:40:02.318Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/b8/c341bba6411bdfda786020343c47a75ef472f6085caf82391b142b1a3ad9/httpx2-2.7.0-py3-none-any.whl", hash = "sha256:ed2a2719c696789e09493bd8e2bec3d8bd925cc6e26b68389ec25ade132f7bf4", size = 90234, upload-time = "2026-07-14T20:39:59.531Z" }, +] + [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", + "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", + "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", + "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", + "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + [[package]] name = "importlib-metadata" version = "8.7.1" @@ -2221,7 +2279,8 @@ source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, - { name = "idna" }, + { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "idna", version = "3.18", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] @@ -2725,6 +2784,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] +[[package]] +name = "truststore" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" }, +] + [[package]] name = "types-awscrt" version = "0.31.3" @@ -2973,7 +3041,7 @@ resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ - { name = "idna", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "idna", version = "3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "multidict", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "propcache", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] @@ -3124,7 +3192,7 @@ resolution-markers = [ "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ - { name = "idna", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, + { name = "idna", version = "3.18", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "multidict", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "propcache", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ]