swarmauri_transport_httpx 0.1.0.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ Copyright 2024 Jacob Stewart
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: swarmauri_transport_httpx
3
+ Version: 0.1.0.dev4
4
+ Summary: Synchronous, asynchronous, buffered, and streaming HTTPX transport for Swarmauri.
5
+ License: Copyright 2024 Jacob Stewart
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
18
+ License-File: LICENSE
19
+ Keywords: swarmauri,httpx,http,streaming,async
20
+ Author: Jacob Stewart
21
+ Author-email: jacob@swarmauri.com
22
+ Requires-Python: >=3.10,<3.13
23
+ Classifier: Development Status :: 1 - Planning
24
+ Classifier: License :: OSI Approved :: Apache Software License
25
+ Classifier: Programming Language :: Python :: 3.10
26
+ Classifier: Programming Language :: Python :: 3.11
27
+ Classifier: Programming Language :: Python :: 3.12
28
+ Requires-Dist: httpx (>=0.27)
29
+ Requires-Dist: swarmauri_base (>=0.11.0.dev5)
30
+ Requires-Dist: swarmauri_core (>=0.11.0.dev5)
31
+ Description-Content-Type: text/markdown
32
+
33
+ ![Swarmauri](https://github.com/swarmauri/swarmauri-sdk/blob/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
34
+
35
+ [![PyPI version](https://img.shields.io/pypi/v/swarmauri_transport_httpx.svg)](https://pypi.org/project/swarmauri_transport_httpx/) [![Python](https://img.shields.io/pypi/pyversions/swarmauri_transport_httpx.svg)](https://pypi.org/project/swarmauri_transport_httpx/) [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE) [![Downloads](https://static.pepy.tech/badge/swarmauri_transport_httpx)](https://pepy.tech/project/swarmauri_transport_httpx) [![Hits](https://hits.sh/github.com/swarmauri/swarmauri-sdk.svg)](https://hits.sh/github.com/swarmauri/swarmauri-sdk/)
36
+
37
+ # swarmauri_transport_httpx
38
+
39
+ ## Features
40
+
41
+ - Synchronous and asynchronous buffered requests.
42
+ - Genuine synchronous and asynchronous streaming contexts.
43
+ - Persistent connection pooling with explicit ownership and cleanup.
44
+ - Caller-supplied HTTPX clients for testing and custom network stacks.
45
+ - Provider-neutral Swarmauri HTTP interfaces.
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ uv add swarmauri_transport_httpx
51
+ pip install swarmauri_transport_httpx
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ Use `HttpxClient.request`/`arequest` for buffered responses and `stream`/`astream` for incremental response processing.
57
+
58
+ ```python
59
+ from swarmauri_transport_httpx import HttpxClient
60
+
61
+ with HttpxClient(base_url="https://api.example.com") as client:
62
+ response = client.request("GET", "/models")
63
+ response.raise_for_status()
64
+ ```
65
+
66
+ Injected `httpx.Client` and `httpx.AsyncClient` instances remain caller-owned and are never closed by the adapter.
67
+
@@ -0,0 +1,34 @@
1
+ ![Swarmauri](https://github.com/swarmauri/swarmauri-sdk/blob/3d4d1cfa949399d7019ae9d8f296afba773dfb7f/assets/swarmauri.brand.theme.svg)
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/swarmauri_transport_httpx.svg)](https://pypi.org/project/swarmauri_transport_httpx/) [![Python](https://img.shields.io/pypi/pyversions/swarmauri_transport_httpx.svg)](https://pypi.org/project/swarmauri_transport_httpx/) [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE) [![Downloads](https://static.pepy.tech/badge/swarmauri_transport_httpx)](https://pepy.tech/project/swarmauri_transport_httpx) [![Hits](https://hits.sh/github.com/swarmauri/swarmauri-sdk.svg)](https://hits.sh/github.com/swarmauri/swarmauri-sdk/)
4
+
5
+ # swarmauri_transport_httpx
6
+
7
+ ## Features
8
+
9
+ - Synchronous and asynchronous buffered requests.
10
+ - Genuine synchronous and asynchronous streaming contexts.
11
+ - Persistent connection pooling with explicit ownership and cleanup.
12
+ - Caller-supplied HTTPX clients for testing and custom network stacks.
13
+ - Provider-neutral Swarmauri HTTP interfaces.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ uv add swarmauri_transport_httpx
19
+ pip install swarmauri_transport_httpx
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ Use `HttpxClient.request`/`arequest` for buffered responses and `stream`/`astream` for incremental response processing.
25
+
26
+ ```python
27
+ from swarmauri_transport_httpx import HttpxClient
28
+
29
+ with HttpxClient(base_url="https://api.example.com") as client:
30
+ response = client.request("GET", "/models")
31
+ response.raise_for_status()
32
+ ```
33
+
34
+ Injected `httpx.Client` and `httpx.AsyncClient` instances remain caller-owned and are never closed by the adapter.
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "swarmauri_transport_httpx"
3
+ version = "0.1.0.dev4"
4
+ description = "Synchronous, asynchronous, buffered, and streaming HTTPX transport for Swarmauri."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10,<3.13"
7
+ authors = [{ name = "Jacob Stewart", email = "jacob@swarmauri.com" }]
8
+ license = { file = "LICENSE" }
9
+ dependencies = ["httpx>=0.27", "swarmauri_core>=0.11.0.dev5", "swarmauri_base>=0.11.0.dev5"]
10
+ classifiers = [
11
+ "Development Status :: 1 - Planning",
12
+ "License :: OSI Approved :: Apache Software License",
13
+ "Programming Language :: Python :: 3.10",
14
+ "Programming Language :: Python :: 3.11",
15
+ "Programming Language :: Python :: 3.12",
16
+ ]
17
+ keywords = ["swarmauri", "httpx", "http", "streaming", "async"]
18
+
19
+ [tool.uv.sources]
20
+ swarmauri_core = { workspace = true }
21
+ swarmauri_base = { workspace = true }
22
+
23
+ [project.entry-points."swarmauri.transports"]
24
+ HttpxClient = "swarmauri_transport_httpx:HttpxClient"
25
+
26
+ [build-system]
27
+ requires = ["poetry-core>=1.8.0"]
28
+ build-backend = "poetry.core.masonry.api"
29
+
30
+ [dependency-groups]
31
+ dev = ["pytest>=8.0", "pytest-asyncio>=0.24", "ruff>=0.9.9"]
32
+
33
+ [tool.ruff]
34
+ extend = "../../pyproject.toml"
35
+
36
+ [tool.ruff.lint]
37
+ ignore = ["N999", "SIM117"]
@@ -0,0 +1,42 @@
1
+ from collections.abc import AsyncIterator
2
+ from typing import Any
3
+
4
+ import httpx
5
+ from pydantic import PrivateAttr
6
+ from swarmauri_base.transports.http.AsyncHttpStreamResponseBase import (
7
+ AsyncHttpStreamResponseBase,
8
+ )
9
+ from swarmauri_core.transports.http_errors import HttpStatusError
10
+
11
+
12
+ class AsyncHttpxStreamResponse(AsyncHttpStreamResponseBase):
13
+ _response: Any = PrivateAttr(default=None)
14
+
15
+ @classmethod
16
+ def from_response(cls, response: Any) -> "AsyncHttpxStreamResponse":
17
+ result = cls(
18
+ status_code=response.status_code,
19
+ headers=dict(response.headers),
20
+ )
21
+ result._response = response
22
+ return result
23
+
24
+ async def aiter_bytes(self) -> AsyncIterator[bytes]:
25
+ async for chunk in self._response.aiter_bytes():
26
+ yield chunk
27
+
28
+ async def aread(self) -> bytes:
29
+ return await self._response.aread()
30
+
31
+ def json(self) -> Any:
32
+ return self._response.json()
33
+
34
+ def raise_for_status(self) -> None:
35
+ try:
36
+ self._response.raise_for_status()
37
+ except httpx.HTTPStatusError as error:
38
+ raise HttpStatusError(
39
+ str(error),
40
+ status_code=self.status_code,
41
+ response=self,
42
+ ) from error
@@ -0,0 +1,171 @@
1
+ from collections.abc import AsyncIterator, Iterator, Mapping
2
+ from contextlib import asynccontextmanager, contextmanager
3
+ from typing import Any
4
+
5
+ import httpx
6
+ from pydantic import Field, PrivateAttr
7
+ from swarmauri_base.transports.http.HttpClientBase import HttpClientBase
8
+ from swarmauri_core.transports.http_errors import HttpRequestError
9
+ from swarmauri_core.transports.i_managed_http_client import IManagedHttpClient
10
+
11
+ from .AsyncHttpxStreamResponse import AsyncHttpxStreamResponse
12
+ from .HttpxResponse import HttpxResponse
13
+ from .HttpxStreamResponse import HttpxStreamResponse
14
+
15
+
16
+ class HttpxClient(HttpClientBase, IManagedHttpClient):
17
+ """Persistent HTTPX adapter for the Swarmauri HTTP client contracts."""
18
+
19
+ client_options: dict[str, Any] = Field(default_factory=dict)
20
+ _sync_client: httpx.Client | None = PrivateAttr(default=None)
21
+ _async_client: httpx.AsyncClient | None = PrivateAttr(default=None)
22
+ _owns_sync_client: bool = PrivateAttr(default=False)
23
+ _owns_async_client: bool = PrivateAttr(default=False)
24
+
25
+ def __init__(
26
+ self,
27
+ *,
28
+ client: httpx.Client | None = None,
29
+ async_client: httpx.AsyncClient | None = None,
30
+ **data: Any,
31
+ ) -> None:
32
+ super().__init__(**data)
33
+ self._sync_client = client
34
+ self._async_client = async_client
35
+
36
+ def _sync(self) -> httpx.Client:
37
+ if self._sync_client is None:
38
+ options = dict(self.client_options)
39
+ options.setdefault("timeout", self.timeout)
40
+ self._sync_client = httpx.Client(**options)
41
+ self._owns_sync_client = True
42
+ return self._sync_client
43
+
44
+ def _async(self) -> httpx.AsyncClient:
45
+ if self._async_client is None:
46
+ options = dict(self.client_options)
47
+ options.setdefault("timeout", self.timeout)
48
+ self._async_client = httpx.AsyncClient(**options)
49
+ self._owns_async_client = True
50
+ return self._async_client
51
+
52
+ @staticmethod
53
+ def _request_kwargs(body: bytes, kwargs: dict[str, Any]) -> dict[str, Any]:
54
+ request_kwargs = dict(kwargs)
55
+ if body:
56
+ if any(
57
+ key in request_kwargs
58
+ for key in ("content", "data", "files", "json")
59
+ ):
60
+ raise ValueError(
61
+ "body cannot be combined with content, data, files, "
62
+ "or json"
63
+ )
64
+ request_kwargs["content"] = body
65
+ return request_kwargs
66
+
67
+ def request(
68
+ self,
69
+ method: str,
70
+ path: str,
71
+ *,
72
+ headers: Mapping[str, str] | None = None,
73
+ body: bytes = b"",
74
+ **kwargs: Any,
75
+ ) -> HttpxResponse:
76
+ try:
77
+ response = self._sync().request(
78
+ method,
79
+ self.resolve_url(path),
80
+ headers=self.merge_headers(dict(headers or {})),
81
+ **self._request_kwargs(body, kwargs),
82
+ )
83
+ return HttpxResponse.from_response(response)
84
+ except httpx.RequestError as error:
85
+ raise HttpRequestError(str(error)) from error
86
+
87
+ async def arequest(
88
+ self,
89
+ method: str,
90
+ path: str,
91
+ *,
92
+ headers: Mapping[str, str] | None = None,
93
+ body: bytes = b"",
94
+ **kwargs: Any,
95
+ ) -> HttpxResponse:
96
+ try:
97
+ response = await self._async().request(
98
+ method,
99
+ self.resolve_url(path),
100
+ headers=self.merge_headers(dict(headers or {})),
101
+ **self._request_kwargs(body, kwargs),
102
+ )
103
+ return HttpxResponse.from_response(response)
104
+ except httpx.RequestError as error:
105
+ raise HttpRequestError(str(error)) from error
106
+
107
+ @contextmanager
108
+ def stream(
109
+ self,
110
+ method: str,
111
+ path: str,
112
+ *,
113
+ headers: Mapping[str, str] | None = None,
114
+ body: bytes = b"",
115
+ **kwargs: Any,
116
+ ) -> Iterator[HttpxStreamResponse]:
117
+ try:
118
+ with self._sync().stream(
119
+ method,
120
+ self.resolve_url(path),
121
+ headers=self.merge_headers(dict(headers or {})),
122
+ **self._request_kwargs(body, kwargs),
123
+ ) as response:
124
+ yield HttpxStreamResponse.from_response(response)
125
+ except httpx.RequestError as error:
126
+ raise HttpRequestError(str(error)) from error
127
+
128
+ @asynccontextmanager
129
+ async def astream(
130
+ self,
131
+ method: str,
132
+ path: str,
133
+ *,
134
+ headers: Mapping[str, str] | None = None,
135
+ body: bytes = b"",
136
+ **kwargs: Any,
137
+ ) -> AsyncIterator[AsyncHttpxStreamResponse]:
138
+ try:
139
+ async with self._async().stream(
140
+ method,
141
+ self.resolve_url(path),
142
+ headers=self.merge_headers(dict(headers or {})),
143
+ **self._request_kwargs(body, kwargs),
144
+ ) as response:
145
+ yield AsyncHttpxStreamResponse.from_response(response)
146
+ except httpx.RequestError as error:
147
+ raise HttpRequestError(str(error)) from error
148
+
149
+ def close(self) -> None:
150
+ if self._sync_client is not None and self._owns_sync_client:
151
+ self._sync_client.close()
152
+ self._sync_client = None
153
+ self._owns_sync_client = False
154
+
155
+ async def aclose(self) -> None:
156
+ if self._async_client is not None and self._owns_async_client:
157
+ await self._async_client.aclose()
158
+ self._async_client = None
159
+ self._owns_async_client = False
160
+
161
+ def __enter__(self) -> "HttpxClient":
162
+ return self
163
+
164
+ def __exit__(self, *args: Any) -> None:
165
+ self.close()
166
+
167
+ async def __aenter__(self) -> "HttpxClient":
168
+ return self
169
+
170
+ async def __aexit__(self, *args: Any) -> None:
171
+ await self.aclose()
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ import httpx
6
+ from pydantic import PrivateAttr
7
+ from swarmauri_base.transports.http.HttpResponseBase import HttpResponseBase
8
+ from swarmauri_core.transports.http_errors import HttpStatusError
9
+
10
+
11
+ class HttpxResponse(HttpResponseBase):
12
+ _response: Any = PrivateAttr(default=None)
13
+
14
+ @classmethod
15
+ def from_response(cls, response: Any) -> HttpxResponse:
16
+ result = cls(
17
+ status_code=response.status_code,
18
+ headers=dict(response.headers),
19
+ )
20
+ result._response = response
21
+ result.set_content(response.content)
22
+ return result
23
+
24
+ def raise_for_status(self) -> None:
25
+ try:
26
+ self._response.raise_for_status()
27
+ except httpx.HTTPStatusError as error:
28
+ raise HttpStatusError(
29
+ str(error),
30
+ status_code=self.status_code,
31
+ response=self,
32
+ ) from error
@@ -0,0 +1,41 @@
1
+ from collections.abc import Iterator
2
+ from typing import Any
3
+
4
+ import httpx
5
+ from pydantic import PrivateAttr
6
+ from swarmauri_base.transports.http.HttpStreamResponseBase import (
7
+ HttpStreamResponseBase,
8
+ )
9
+ from swarmauri_core.transports.http_errors import HttpStatusError
10
+
11
+
12
+ class HttpxStreamResponse(HttpStreamResponseBase):
13
+ _response: Any = PrivateAttr(default=None)
14
+
15
+ @classmethod
16
+ def from_response(cls, response: Any) -> "HttpxStreamResponse":
17
+ result = cls(
18
+ status_code=response.status_code,
19
+ headers=dict(response.headers),
20
+ )
21
+ result._response = response
22
+ return result
23
+
24
+ def iter_bytes(self) -> Iterator[bytes]:
25
+ yield from self._response.iter_bytes()
26
+
27
+ def read(self) -> bytes:
28
+ return self._response.read()
29
+
30
+ def json(self) -> Any:
31
+ return self._response.json()
32
+
33
+ def raise_for_status(self) -> None:
34
+ try:
35
+ self._response.raise_for_status()
36
+ except httpx.HTTPStatusError as error:
37
+ raise HttpStatusError(
38
+ str(error),
39
+ status_code=self.status_code,
40
+ response=self,
41
+ ) from error
@@ -0,0 +1,11 @@
1
+ from .AsyncHttpxStreamResponse import AsyncHttpxStreamResponse
2
+ from .HttpxClient import HttpxClient
3
+ from .HttpxResponse import HttpxResponse
4
+ from .HttpxStreamResponse import HttpxStreamResponse
5
+
6
+ __all__ = [
7
+ "AsyncHttpxStreamResponse",
8
+ "HttpxClient",
9
+ "HttpxResponse",
10
+ "HttpxStreamResponse",
11
+ ]