httpware 0.1.0__tar.gz → 0.3.0__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,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: httpware
3
+ Version: 0.3.0
4
+ Summary: Resilience-first async HTTP client framework for Python
5
+ Keywords: http,async,client,resilience,retry,circuit-breaker,middleware,httpx,pydantic
6
+ Author: Artur Shiriev
7
+ Author-email: Artur Shiriev <me@shiriev.ru>
8
+ License-Expression: MIT
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Typing :: Typed
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Classifier: Topic :: Internet :: WWW/HTTP
16
+ Classifier: Framework :: AsyncIO
17
+ Requires-Dist: httpx2>=2.0.0,<3.0
18
+ Requires-Dist: httpware[pydantic,msgspec,otel] ; extra == 'all'
19
+ Requires-Dist: msgspec>=0.18 ; extra == 'msgspec'
20
+ Requires-Dist: opentelemetry-api>=1.20 ; extra == 'otel'
21
+ Requires-Dist: opentelemetry-sdk>=1.20 ; extra == 'otel'
22
+ Requires-Dist: pydantic>=2.0,<3.0 ; extra == 'pydantic'
23
+ Requires-Python: >=3.11, <4
24
+ Project-URL: repository, https://github.com/modern-python/httpware
25
+ Project-URL: docs, https://httpware.readthedocs.io
26
+ Provides-Extra: all
27
+ Provides-Extra: msgspec
28
+ Provides-Extra: otel
29
+ Provides-Extra: pydantic
30
+ Description-Content-Type: text/markdown
31
+
32
+ # httpware
33
+
34
+ [![Test](https://github.com/modern-python/httpware/actions/workflows/ci.yml/badge.svg)](https://github.com/modern-python/httpware/actions/workflows/ci.yml)
35
+ [![PyPI version](https://badge.fury.io/py/httpware.svg)](https://pypi.org/project/httpware/)
36
+ [![Python versions](https://img.shields.io/pypi/pyversions/httpware.svg)](https://pypi.org/project/httpware/)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
38
+
39
+ **Async HTTP client framework for Python.**
40
+
41
+ `httpware` is a thin opinionated wrapper around `httpx2`. It re-exports `httpx2.Request`/`httpx2.Response`, adds a middleware chain composed at client construction, supports opt-in typed response decoding (pydantic and msgspec are both extras), and raises a status-keyed exception tree automatically on 4xx/5xx.
42
+
43
+ > **Status:** Pre-1.0 (0.3.0). Public API is subject to change between minor releases until v1.0. Resilience middleware (retry / timeout / bulkhead), streaming, and observability are not yet shipped.
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install httpware # core only — no decoder
49
+ pip install httpware[pydantic] # + PydanticDecoder (the default-decoder path)
50
+ pip install httpware[msgspec] # + MsgspecDecoder
51
+ pip install httpware[all] # everything declared above (pydantic, msgspec, otel)
52
+ ```
53
+
54
+ `AsyncClient()` with no `decoder=` argument defaults to constructing a `PydanticDecoder`; that path requires the `pydantic` extra and raises `ImportError` at `AsyncClient.__init__` if it is missing. The `otel` extra is declared but the OpenTelemetry middleware (Epic 5) has not shipped yet.
55
+
56
+ ## Quickstart
57
+
58
+ > Requires: `pip install httpware[pydantic]`
59
+
60
+ ```python
61
+ from httpware import AsyncClient
62
+ from pydantic import BaseModel
63
+
64
+
65
+ class User(BaseModel):
66
+ id: int
67
+ name: str
68
+
69
+
70
+ async def main() -> None:
71
+ async with AsyncClient(base_url="https://api.example.com") as client:
72
+ user = await client.get("/users/1", response_model=User)
73
+ print(user.name)
74
+ ```
75
+
76
+ ## 📚 [Documentation](https://httpware.readthedocs.io)
77
+
78
+ ## 📦 [PyPI](https://pypi.org/project/httpware)
79
+
80
+ ## 📝 [License](./LICENSE)
81
+
82
+ ## Part of `modern-python`
83
+
84
+ Browse the full list of templates and libraries in [`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index.
@@ -0,0 +1,53 @@
1
+ # httpware
2
+
3
+ [![Test](https://github.com/modern-python/httpware/actions/workflows/ci.yml/badge.svg)](https://github.com/modern-python/httpware/actions/workflows/ci.yml)
4
+ [![PyPI version](https://badge.fury.io/py/httpware.svg)](https://pypi.org/project/httpware/)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/httpware.svg)](https://pypi.org/project/httpware/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ **Async HTTP client framework for Python.**
9
+
10
+ `httpware` is a thin opinionated wrapper around `httpx2`. It re-exports `httpx2.Request`/`httpx2.Response`, adds a middleware chain composed at client construction, supports opt-in typed response decoding (pydantic and msgspec are both extras), and raises a status-keyed exception tree automatically on 4xx/5xx.
11
+
12
+ > **Status:** Pre-1.0 (0.3.0). Public API is subject to change between minor releases until v1.0. Resilience middleware (retry / timeout / bulkhead), streaming, and observability are not yet shipped.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install httpware # core only — no decoder
18
+ pip install httpware[pydantic] # + PydanticDecoder (the default-decoder path)
19
+ pip install httpware[msgspec] # + MsgspecDecoder
20
+ pip install httpware[all] # everything declared above (pydantic, msgspec, otel)
21
+ ```
22
+
23
+ `AsyncClient()` with no `decoder=` argument defaults to constructing a `PydanticDecoder`; that path requires the `pydantic` extra and raises `ImportError` at `AsyncClient.__init__` if it is missing. The `otel` extra is declared but the OpenTelemetry middleware (Epic 5) has not shipped yet.
24
+
25
+ ## Quickstart
26
+
27
+ > Requires: `pip install httpware[pydantic]`
28
+
29
+ ```python
30
+ from httpware import AsyncClient
31
+ from pydantic import BaseModel
32
+
33
+
34
+ class User(BaseModel):
35
+ id: int
36
+ name: str
37
+
38
+
39
+ async def main() -> None:
40
+ async with AsyncClient(base_url="https://api.example.com") as client:
41
+ user = await client.get("/users/1", response_model=User)
42
+ print(user.name)
43
+ ```
44
+
45
+ ## 📚 [Documentation](https://httpware.readthedocs.io)
46
+
47
+ ## 📦 [PyPI](https://pypi.org/project/httpware)
48
+
49
+ ## 📝 [License](./LICENSE)
50
+
51
+ ## Part of `modern-python`
52
+
53
+ Browse the full list of templates and libraries in [`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index.
@@ -26,27 +26,26 @@ classifiers = [
26
26
  "Topic :: Internet :: WWW/HTTP",
27
27
  "Framework :: AsyncIO",
28
28
  ]
29
- version = "0.1.0"
29
+ version = "0.3.0"
30
30
  dependencies = [
31
31
  "httpx2>=2.0.0,<3.0",
32
- "pydantic>=2.0,<3.0",
33
32
  ]
34
33
 
35
34
  [project.optional-dependencies]
35
+ pydantic = ["pydantic>=2.0,<3.0"]
36
36
  msgspec = ["msgspec>=0.18"]
37
37
  otel = [
38
38
  "opentelemetry-api>=1.20",
39
39
  "opentelemetry-sdk>=1.20",
40
40
  ]
41
- niquests = ["niquests>=3.18"]
42
- all = ["httpware[msgspec,otel,niquests]"]
41
+ all = ["httpware[pydantic,msgspec,otel]"]
43
42
 
44
43
  [project.urls]
45
44
  repository = "https://github.com/modern-python/httpware"
46
45
  docs = "https://httpware.readthedocs.io"
47
46
 
48
47
  [build-system]
49
- requires = ["uv_build>=0.11,<0.12"]
48
+ requires = ["uv_build>=0.11,<1.0"]
50
49
  build-backend = "uv_build"
51
50
 
52
51
  [tool.uv.build-backend]
@@ -74,7 +73,6 @@ fix = true
74
73
  unsafe-fixes = true
75
74
  line-length = 120
76
75
  target-version = "py311"
77
- extend-exclude = ["docs"]
78
76
 
79
77
  [tool.ruff.lint]
80
78
  select = ["ALL"]
@@ -90,24 +88,16 @@ ignore = [
90
88
  ]
91
89
  isort.lines-after-imports = 2
92
90
  isort.no-lines-before = ["standard-library", "local-folder"]
93
- pylint.max-args = 10 # HTTP-method APIs are kwarg-rich (headers, params, cookies, timeout, json, content, response_model, …); default 5 is too strict.
94
91
 
95
92
  [tool.ruff.lint.per-file-ignores]
96
- # AsyncClient's HTTP-method `timeout=` is a config-value parameter forwarded to the transport,
97
- # not asyncio.timeout territory. The rule fires on 24+ method signatures in this one file with
98
- # the same false-positive justification; per-file disable is cleaner than 24 per-line noqa.
99
- "src/httpware/client.py" = ["ASYNC109"]
93
+ "src/httpware/client.py" = ["ASYNC109", "ANN401"]
100
94
 
101
95
  [tool.pytest.ini_options]
102
- addopts = "--cov=src/httpware --cov-report term-missing -m 'not perf'"
96
+ addopts = "--cov=. --cov-report term-missing --cov-fail-under=100"
103
97
  asyncio_mode = "auto"
104
98
  pythonpath = ["src"]
105
99
  asyncio_default_fixture_loop_scope = "function"
106
- markers = [
107
- "perf: assertive performance tests (skipped by default; run with `pytest -m perf`)",
108
- ]
109
100
 
110
101
  [tool.coverage]
111
102
  run.concurrency = ["thread"]
112
- run.omit = ["benchmarks/*"]
113
103
  report.exclude_also = ["if typing.TYPE_CHECKING:"]
@@ -1,9 +1,7 @@
1
- """httpware — resilience-first async HTTP client framework for Python."""
1
+ """httpware — thin async HTTP client wrapper over httpx2."""
2
2
 
3
3
  from httpware.client import AsyncClient
4
- from httpware.config import ClientConfig, Limits, Timeout
5
4
  from httpware.decoders import ResponseDecoder
6
- from httpware.decoders.pydantic import PydanticDecoder
7
5
  from httpware.errors import (
8
6
  STATUS_TO_EXCEPTION,
9
7
  BadRequestError,
@@ -23,41 +21,26 @@ from httpware.errors import (
23
21
  UnprocessableEntityError,
24
22
  )
25
23
  from httpware.middleware import Middleware, Next, after_response, before_request, on_error
26
- from httpware.request import Request
27
- from httpware.response import Response, StreamResponse
28
- from httpware.transports import Transport
29
- from httpware.transports.httpx2 import Httpx2Transport
30
- from httpware.transports.recorded import RecordedTransport
31
24
 
32
25
 
33
26
  __all__ = [
34
27
  "STATUS_TO_EXCEPTION",
35
28
  "AsyncClient",
36
29
  "BadRequestError",
37
- "ClientConfig",
38
30
  "ClientError",
39
31
  "ClientStatusError",
40
32
  "ConflictError",
41
33
  "ForbiddenError",
42
- "Httpx2Transport",
43
34
  "InternalServerError",
44
- "Limits",
45
35
  "Middleware",
46
36
  "Next",
47
37
  "NotFoundError",
48
- "PydanticDecoder",
49
38
  "RateLimitedError",
50
- "RecordedTransport",
51
- "Request",
52
- "Response",
53
39
  "ResponseDecoder",
54
40
  "ServerStatusError",
55
41
  "ServiceUnavailableError",
56
42
  "StatusError",
57
- "StreamResponse",
58
- "Timeout",
59
43
  "TimeoutError",
60
- "Transport",
61
44
  "TransportError",
62
45
  "UnauthorizedError",
63
46
  "UnprocessableEntityError",
@@ -4,3 +4,4 @@ from importlib.util import find_spec
4
4
 
5
5
 
6
6
  is_msgspec_installed = find_spec("msgspec") is not None
7
+ is_pydantic_installed = find_spec("pydantic") is not None