httpxgen 0.2.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.
- {httpxgen-0.2.0 → httpxgen-0.3.0}/.github/workflows/ci.yml +68 -64
- {httpxgen-0.2.0 → httpxgen-0.3.0}/PKG-INFO +133 -7
- {httpxgen-0.2.0 → httpxgen-0.3.0}/README.md +679 -553
- httpxgen-0.3.0/changelog.md +298 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/build.py +222 -195
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/client.py +234 -81
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/models.py +144 -27
- httpxgen-0.3.0/httpxgen/generator/naming.py +97 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/normalize.py +692 -665
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/operations.py +367 -110
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/package.py +279 -258
- httpxgen-0.3.0/httpxgen/generator/schema.py +226 -0
- httpxgen-0.3.0/httpxgen/generator/templates/binary_operation.jinja2 +51 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/client.jinja2 +29 -29
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/exceptions.jinja2 +81 -74
- httpxgen-0.3.0/httpxgen/generator/templates/serialization.jinja2 +287 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/shared_init.jinja2 +67 -51
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/transport.jinja2 +155 -112
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates.py +42 -42
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/openapi.py +118 -117
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/output.py +27 -2
- httpxgen-0.3.0/httpxgen/references.py +149 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/selection.py +66 -50
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/invoices/client.py +17 -10
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/payments/client.py +60 -30
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/payments/models.py +21 -7
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/shared/__init__.py +16 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/shared/exceptions.py +8 -1
- httpxgen-0.3.0/preview/shared/serialization.py +294 -0
- httpxgen-0.3.0/preview/shared/transport.py +162 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/pyproject.toml +1 -1
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_build.py +34 -33
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_client.py +5 -5
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_improvements.py +351 -340
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_normalize.py +3 -5
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_schema.py +94 -92
- httpxgen-0.3.0/tests/generator/test_tickets.py +2634 -0
- httpxgen-0.3.0/tests/generator/test_typing.py +114 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/test_output.py +50 -2
- {httpxgen-0.2.0 → httpxgen-0.3.0}/uv.lock +546 -546
- httpxgen-0.2.0/IMPROVEMENT_SPEC.md +0 -483
- httpxgen-0.2.0/httpxgen/generator/naming.py +0 -40
- httpxgen-0.2.0/httpxgen/generator/schema.py +0 -142
- httpxgen-0.2.0/httpxgen/generator/templates/binary_operation.jinja2 +0 -51
- httpxgen-0.2.0/httpxgen/generator/templates/serialization.jinja2 +0 -130
- httpxgen-0.2.0/preview/shared/serialization.py +0 -137
- httpxgen-0.2.0/preview/shared/transport.py +0 -109
- {httpxgen-0.2.0 → httpxgen-0.3.0}/.gitignore +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/.python-version +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/CLAUDE.md +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/LICENSE +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/VISION.md +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/__init__.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/cli.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/__init__.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/errors.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/enum.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/generated_header.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/http_methods.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/model.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/models.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/operation.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/generator/templates/package_init.jinja2 +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/io.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/httpxgen/loading.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/__init__.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/invoices/__init__.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/invoices/models.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/payments/__init__.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/py.typed +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/shared/http_methods.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/preview/shared/models.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/scripts/generate-preview.sh +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/specs/api.yml +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/conftest.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_models.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_naming.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_operations.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_package.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/generator/test_runtime.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/test_cli.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/test_loading.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/test_openapi.py +0 -0
- {httpxgen-0.2.0 → httpxgen-0.3.0}/tests/test_selection.py +0 -0
|
@@ -1,64 +1,68 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
lint:
|
|
10
|
-
runs-on: ubuntu-latest
|
|
11
|
-
steps:
|
|
12
|
-
- uses: actions/checkout@v4
|
|
13
|
-
|
|
14
|
-
- name: Install uv
|
|
15
|
-
uses: astral-sh/setup-uv@v4
|
|
16
|
-
with:
|
|
17
|
-
python-version: "3.14"
|
|
18
|
-
|
|
19
|
-
- name: Install dependencies
|
|
20
|
-
run: uv sync --all-groups
|
|
21
|
-
|
|
22
|
-
- name: Ruff lint
|
|
23
|
-
run: uv run ruff check --output-format=github .
|
|
24
|
-
|
|
25
|
-
- name: Ruff format
|
|
26
|
-
run: uv run ruff format --check .
|
|
27
|
-
|
|
28
|
-
preview:
|
|
29
|
-
runs-on: ubuntu-latest
|
|
30
|
-
steps:
|
|
31
|
-
- uses: actions/checkout@v4
|
|
32
|
-
|
|
33
|
-
- name: Install uv
|
|
34
|
-
uses: astral-sh/setup-uv@v4
|
|
35
|
-
with:
|
|
36
|
-
python-version: "3.14"
|
|
37
|
-
|
|
38
|
-
- name: Install dependencies
|
|
39
|
-
run: uv sync --all-groups
|
|
40
|
-
|
|
41
|
-
- name: Regenerate the preview client
|
|
42
|
-
run: bash scripts/generate-preview.sh
|
|
43
|
-
|
|
44
|
-
- name: Fail if the checked-in preview is stale
|
|
45
|
-
run: git diff --exit-code -- preview
|
|
46
|
-
|
|
47
|
-
test:
|
|
48
|
-
runs-on: ubuntu-latest
|
|
49
|
-
strategy:
|
|
50
|
-
matrix:
|
|
51
|
-
python-version: ["3.12", "3.13", "3.14"]
|
|
52
|
-
steps:
|
|
53
|
-
- uses: actions/checkout@v4
|
|
54
|
-
|
|
55
|
-
- name: Install uv
|
|
56
|
-
uses: astral-sh/setup-uv@v4
|
|
57
|
-
with:
|
|
58
|
-
python-version: ${{ matrix.python-version }}
|
|
59
|
-
|
|
60
|
-
- name: Install dependencies
|
|
61
|
-
run: uv sync --all-groups
|
|
62
|
-
|
|
63
|
-
- name: Run tests
|
|
64
|
-
run: uv run pytest
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Install uv
|
|
15
|
+
uses: astral-sh/setup-uv@v4
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.14"
|
|
18
|
+
|
|
19
|
+
- name: Install dependencies
|
|
20
|
+
run: uv sync --locked --all-groups
|
|
21
|
+
|
|
22
|
+
- name: Ruff lint
|
|
23
|
+
run: uv run ruff check --output-format=github .
|
|
24
|
+
|
|
25
|
+
- name: Ruff format
|
|
26
|
+
run: uv run ruff format --check .
|
|
27
|
+
|
|
28
|
+
preview:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v4
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.14"
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: uv sync --locked --all-groups
|
|
40
|
+
|
|
41
|
+
- name: Regenerate the preview client
|
|
42
|
+
run: bash scripts/generate-preview.sh
|
|
43
|
+
|
|
44
|
+
- name: Fail if the checked-in preview is stale
|
|
45
|
+
run: git diff --exit-code -- preview
|
|
46
|
+
|
|
47
|
+
test:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
strategy:
|
|
50
|
+
matrix:
|
|
51
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
|
|
55
|
+
- name: Install uv
|
|
56
|
+
uses: astral-sh/setup-uv@v4
|
|
57
|
+
with:
|
|
58
|
+
python-version: ${{ matrix.python-version }}
|
|
59
|
+
|
|
60
|
+
- name: Install dependencies
|
|
61
|
+
run: uv sync --locked --all-groups
|
|
62
|
+
|
|
63
|
+
- name: Run tests
|
|
64
|
+
run: uv run pytest
|
|
65
|
+
|
|
66
|
+
- name: Type-check generated protocols
|
|
67
|
+
if: matrix.python-version == '3.14'
|
|
68
|
+
run: uv run --frozen --with mypy==2.4.0 --with httpx2==2.13.1 pytest --no-cov tests/generator/test_typing.py
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: httpxgen
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Generate a typed async httpx client from an OpenAPI document.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -291,7 +291,23 @@ Path parameters carry their spec format — `format: uuid` becomes `UUID`, not `
|
|
|
291
291
|
raise ApiError(response.status_code, response.text, response=response)
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
-
Request bodies are a single typed `body` argument, serialized by alias
|
|
294
|
+
Request bodies are a single typed `body` argument, serialized by alias.
|
|
295
|
+
JSON inputs are validated using the declared Pydantic schema before the HTTP
|
|
296
|
+
transport runs, including mappings and model instances changed after creation.
|
|
297
|
+
Invalid inputs raise `RequestValidationError`; Pydantic's normal accepted
|
|
298
|
+
conversions apply before serialization.
|
|
299
|
+
Unsupported runtime `content_type` choices also raise `RequestValidationError`
|
|
300
|
+
before sending a request, with the declared alternatives listed.
|
|
301
|
+
Explicitly supplied nullable fields are sent as JSON null; unset nullable fields
|
|
302
|
+
are omitted, including inside nested models:
|
|
303
|
+
|
|
304
|
+
For `application/merge-patch+json`, all unset model fields are omitted, including
|
|
305
|
+
schema defaults. Explicit values equal to a default are still sent. Ordinary
|
|
306
|
+
JSON create requests continue to include non-null defaults.
|
|
307
|
+
|
|
308
|
+
Omit an optional JSON body by leaving out `body`. Passing `body=None` sends the
|
|
309
|
+
literal JSON payload `null` when the schema permits null, with the selected JSON
|
|
310
|
+
Content-Type. Omission uses a separate generated sentinel.
|
|
295
311
|
|
|
296
312
|
```python
|
|
297
313
|
async def create_charge(
|
|
@@ -301,9 +317,7 @@ Request bodies are a single typed `body` argument, serialized by alias and witho
|
|
|
301
317
|
timeout: float | None = None,
|
|
302
318
|
) -> Charge:
|
|
303
319
|
...
|
|
304
|
-
json_body =
|
|
305
|
-
body, mode="json", by_alias=True, exclude_none=True
|
|
306
|
-
)
|
|
320
|
+
json_body = serialize_json_body(body, CreateChargeRequest)
|
|
307
321
|
...
|
|
308
322
|
```
|
|
309
323
|
|
|
@@ -311,10 +325,19 @@ The same direct shape is used for JSON, form, text, and small binary bodies.
|
|
|
311
325
|
An `application/octet-stream` body accepts `bytes`, `Iterable[bytes]`, or
|
|
312
326
|
`AsyncIterable[bytes]` and is passed directly as `content=`.
|
|
313
327
|
|
|
328
|
+
Synchronous binary iterables are adapted lazily to async chunks. Exceptions
|
|
329
|
+
raised while consuming an upload iterable are reported as `TransportError`.
|
|
330
|
+
|
|
314
331
|
Multipart fields are flattened into method arguments. Binary arrays become
|
|
315
|
-
repeated parts with the same wire name
|
|
332
|
+
repeated parts with the same wire name. Parameter and multipart argument names
|
|
333
|
+
that collide with generated locals (such as `path`, `headers`, or `files`) gain
|
|
334
|
+
an `_arg` suffix; their wire names are preserved. `UploadFile` carries optional
|
|
316
335
|
per-part metadata without reading an open file eagerly:
|
|
317
336
|
|
|
337
|
+
Bytes and default `UploadFile` inputs use the filename `upload`; bare handles
|
|
338
|
+
use their basename when available. Set `UploadFile(filename=None, ...)`
|
|
339
|
+
explicitly to send a filename-less part.
|
|
340
|
+
|
|
318
341
|
```python
|
|
319
342
|
await client.upload_documents(
|
|
320
343
|
documents=[
|
|
@@ -332,6 +355,16 @@ call sites to pass fields directly.
|
|
|
332
355
|
Successful binary responses also get a streaming context-manager variant. The
|
|
333
356
|
buffered method remains available for small responses:
|
|
334
357
|
|
|
358
|
+
For endpoints with mixed success representations, buffered methods return the
|
|
359
|
+
declared bytes/model/text/None union. Streaming variants yield `BinaryStream`
|
|
360
|
+
for binary content and the parsed value for JSON, text, or empty successes.
|
|
361
|
+
|
|
362
|
+
Binary successes with declared headers return a typed result whose `body` is
|
|
363
|
+
bytes and whose fields contain validated headers. Their streaming variants
|
|
364
|
+
yield a corresponding `...Result...Stream` model with `body: BinaryStream` and
|
|
365
|
+
the same header fields, validated before iteration. Endpoints without declared
|
|
366
|
+
headers retain the direct bytes/`BinaryStream` API.
|
|
367
|
+
|
|
335
368
|
```python
|
|
336
369
|
content = await client.download_recording(recording_id)
|
|
337
370
|
|
|
@@ -343,6 +376,7 @@ print(recording.media_type, recording.content_length, recording.filename)
|
|
|
343
376
|
```
|
|
344
377
|
|
|
345
378
|
The streaming context owns the individual response only. The generated wrapper
|
|
379
|
+
closes entered responses even when validation or reading is cancelled, and
|
|
346
380
|
treats its injected HTTP client as borrowed, so closing the wrapper does not
|
|
347
381
|
close a shared application transport:
|
|
348
382
|
|
|
@@ -359,10 +393,14 @@ await http_client.aclose()
|
|
|
359
393
|
```
|
|
360
394
|
|
|
361
395
|
All operation failures derive from `ClientError`. `TransportError` retains its
|
|
362
|
-
original `cause`,
|
|
396
|
+
original `cause`, including failures during binary iteration.
|
|
397
|
+
`ResponseValidationError` retains both the response and cause,
|
|
363
398
|
and `ApiError` exposes a parsed `detail` plus convenient `code` and `message`
|
|
364
399
|
properties. `body` and `parsed_body` remain compatibility aliases for
|
|
365
400
|
`raw_body` and `detail`.
|
|
401
|
+
When an error declares headers, `detail` retains its typed result wrapper and
|
|
402
|
+
header fields; `code`/`message` read its typed payload. Arbitrary user payloads
|
|
403
|
+
with a property named `body` are not unwrapped.
|
|
366
404
|
|
|
367
405
|
### Generated models
|
|
368
406
|
|
|
@@ -530,6 +568,57 @@ client-check:
|
|
|
530
568
|
|
|
531
569
|
## Supported scope
|
|
532
570
|
|
|
571
|
+
HTTP header names merge case-insensitively. Configured values override generated
|
|
572
|
+
defaults; operation header parameters override configured values; applied
|
|
573
|
+
security credentials take precedence over both. Each logical header is sent once.
|
|
574
|
+
|
|
575
|
+
Recursive array and `anyOf` aliases use lazy Python 3.12+ type aliases, including
|
|
576
|
+
mutual recursion. Recursive nondiscriminated `oneOf` and reference-only aliases are rejected with
|
|
577
|
+
an actionable error; ordinary aliases and recursive object models stay readable.
|
|
578
|
+
|
|
579
|
+
Exact `oneOf` membership uses JSON types without string/number/boolean coercion.
|
|
580
|
+
Formatted date/UUID strings remain valid, and mathematically integral JSON
|
|
581
|
+
numbers count as integers. Zero or multiple matching branches are rejected.
|
|
582
|
+
|
|
583
|
+
Query names beginning with `_` or `model_` receive a public `field`/`field_`
|
|
584
|
+
prefix in generated signatures and query models. Their wire names and
|
|
585
|
+
validation constraints remain intact.
|
|
586
|
+
|
|
587
|
+
Leading-underscore schema properties also receive a `field` prefix (for example,
|
|
588
|
+
`_id` becomes `field_id`) and keep an explicit wire alias. They are validated
|
|
589
|
+
ordinary model fields, including when additional properties are forbidden.
|
|
590
|
+
|
|
591
|
+
Operation names that collide with generated lifecycle methods or streaming
|
|
592
|
+
companions fail generation before writing files; rename the conflicting
|
|
593
|
+
`operationId` in the specification.
|
|
594
|
+
|
|
595
|
+
Local Path Item references are resolved before schema normalization and tag
|
|
596
|
+
selection. Authored siblings replace the corresponding inherited fields;
|
|
597
|
+
omitted siblings preserve inherited parameters and operations. Cycles fail
|
|
598
|
+
generation with a specific error.
|
|
599
|
+
|
|
600
|
+
References to nullable object components accept null in response bodies, request
|
|
601
|
+
arguments, and nested fields while keeping the object model's required fields
|
|
602
|
+
and constraints. OpenAPI 3.0 `nullable` requires an explicit type.
|
|
603
|
+
|
|
604
|
+
URL-encoded forms honor supported per-field style/explode encodings and JSON
|
|
605
|
+
contentType. Object values use JSON by default; explicit form styles can flatten
|
|
606
|
+
simple objects. Unsupported encoding options (including form allowReserved)
|
|
607
|
+
fail generation.
|
|
608
|
+
|
|
609
|
+
Multipart object fields and object-array items are JSON parts with aliases and
|
|
610
|
+
the declared `encoding.contentType` (JSON/+json), or application/json by default.
|
|
611
|
+
File parts also honor `encoding.contentType`. Unsupported multipart encoding
|
|
612
|
+
options fail generation explicitly.
|
|
613
|
+
|
|
614
|
+
Dot-valued path identifiers (`.` and `..`) are percent-encoded to preserve the
|
|
615
|
+
route boundary instead of allowing URL dot-segment normalization.
|
|
616
|
+
|
|
617
|
+
Path `simple`, `label`, and `matrix` styles support scalar, flat array, and flat
|
|
618
|
+
object values with their style-specific explode delimiters. Data is escaped
|
|
619
|
+
before joining it, including literal commas, label dots, slashes, and percent
|
|
620
|
+
signs. `allowReserved` applies only to query parameters.
|
|
621
|
+
|
|
533
622
|
httpxgen targets ordinary OpenAPI 3.0 and 3.1 client specifications, not every
|
|
534
623
|
JSON Schema feature. It supports JSON/YAML input, local component references,
|
|
535
624
|
path/query/header/cookie serialization, JSON/form/multipart/binary request
|
|
@@ -539,12 +628,41 @@ security schemes, directional request/response models, inline and recursive
|
|
|
539
628
|
Pydantic models, enums, nullable values, discriminated unions, and practical
|
|
540
629
|
`allOf` inheritance.
|
|
541
630
|
|
|
631
|
+
`allOf` supports object-model inheritance. Scalar/array intersections and
|
|
632
|
+
nullable or union bases fail generation with an unsupported-composition error.
|
|
633
|
+
|
|
634
|
+
Required object keys without declared property schemas retain presence checks
|
|
635
|
+
and their additional-property validation, including inherited and directional
|
|
636
|
+
models. Their values remain unconstrained when additional properties are allowed.
|
|
637
|
+
|
|
542
638
|
Unsupported constructs fail generation where possible. Important remaining
|
|
543
639
|
limitations are external references, callbacks/webhooks, automatic
|
|
544
640
|
pagination, and a synchronous client.
|
|
545
641
|
|
|
546
642
|
## Development
|
|
547
643
|
|
|
644
|
+
String `format: byte` values use standard Base64 in JSON (OpenAPI 3.0 and the
|
|
645
|
+
legacy format in 3.1). Python bytes hold decoded data; Base64 strings are decoded
|
|
646
|
+
with strict alphabet/padding validation. Invalid response strings raise
|
|
647
|
+
`ResponseValidationError`. This does not change `format: binary`, raw binary
|
|
648
|
+
bodies, or multipart file uploads.
|
|
649
|
+
|
|
650
|
+
Parameter `content` supports one JSON/+json representation for query, header,
|
|
651
|
+
and path positions. Values are validated and serialized as one compact JSON
|
|
652
|
+
value with aliases and formatted fields. Required nullable parameters send JSON
|
|
653
|
+
null; optional `None` omits the parameter. JSON cookie content and combinations
|
|
654
|
+
with schema/style/explode/allowReserved are rejected during generation.
|
|
655
|
+
|
|
656
|
+
Query `allowReserved: true` preserves `:/?@!$'()*,;` and valid percent-encoded
|
|
657
|
+
triples in values. It escapes `[]#&=+`, stray percent signs, and other disallowed
|
|
658
|
+
characters to preserve query boundaries and form decoding. Parameter names,
|
|
659
|
+
ordinary parameters, and query API keys retain normal escaping.
|
|
660
|
+
|
|
661
|
+
Regeneration removes obsolete modules carrying the generator header inside the
|
|
662
|
+
output package; `--check` reports them without changing files. Handwritten files
|
|
663
|
+
and unmanaged `__init__.py`/`py.typed` files are preserved. Linked output paths
|
|
664
|
+
are rejected instead of writing outside the package.
|
|
665
|
+
|
|
548
666
|
```sh
|
|
549
667
|
uv sync --all-groups
|
|
550
668
|
uv run pytest
|
|
@@ -552,6 +670,14 @@ uv run ruff check . # lint
|
|
|
552
670
|
uv run ruff format . # format
|
|
553
671
|
```
|
|
554
672
|
|
|
673
|
+
CI also checks generated transport/operation typing with Mypy against HTTPX and
|
|
674
|
+
HTTPX2. Run that check locally with:
|
|
675
|
+
|
|
676
|
+
```sh
|
|
677
|
+
uv run --frozen --with mypy==2.4.0 --with httpx2==2.13.1 \
|
|
678
|
+
pytest --no-cov tests/generator/test_typing.py
|
|
679
|
+
```
|
|
680
|
+
|
|
555
681
|
`preview/` is generated output, checked in so the effect of a change is visible
|
|
556
682
|
in review. Regenerate it whenever generated code changes — CI fails if it is
|
|
557
683
|
stale:
|