Render valbridge IR into Pydantic v2 models
Project description
valbridge-pydantic
valbridge adapter for generating Pydantic v2 models from JSON Schema.
Verified against pydantic 2.12.5.
Local Verification
All commands run from the adapter directory (python/packages/adapters/pydantic/).
Compliance (runtime validation)
# from repo root cli/ directory — builds CLI then runs full compliance
cd ../../cli && go build -o valbridge . && ./valbridge compliance --lang python --adapter-path ../python/packages/adapters/pydantic
Unit tests
uv run pytest
Type checking (static)
uv run pyright src/
Fallback Typing Guardrails
Allowed Any fallbacks
These constructs produce Annotated[Any, BeforeValidator(...)] by design. The static type is Any because the semantic domain is unbounded — no Python type expression can represent the true set of valid values.
| Construct | Reason | Example |
|---|---|---|
not |
Negation is "everything except X" — no union can express this | not: { type: "string" } accepts any non-string |
conditional (if/then only, if/else only) |
When only one branch exists, values not matching if pass through unconstrained |
if: {...}, then: {...} with no else |
typeGuarded (heterogeneous dispatch) |
Unmatched types pass through the guard — domain is unbounded | Type guard with object/array branches, but string input passes |
Open tuple (no items: false) |
Extra elements beyond prefix items are untyped | prefixItems: [string, int] without items: false |
| Recursive refs | Self-referencing $ref (cycle detected, path starts with #) |
$ref: "#" or $ref: "#/$defs/TreeNode" |
Narrowed constructs (must NOT use Any)
These constructs must produce a narrower type than Any.
| Construct | Expected type | How it narrows |
|---|---|---|
oneOf |
T1 | T2 | ... |
_union_base_type() computes union from sub-schema types |
conditional (if/then/else) |
ThenType | ElseType |
_union_base_type([then_type, else_type]) |
Closed tuple (items: false) |
tuple[T1 | T2, ...] |
_union_base_type(item_types) on prefix items |
| Tuple with rest schema | tuple[T1 | ... | TRest, ...] |
_union_base_type(item_types + [rest_type]) |
const (object) |
dict |
_json_value_type(value) |
const (array) |
list |
_json_value_type(value) |
const (primitive) |
bool, int, float, str, None |
_json_value_type(value) |
enum (complex values) |
dict | str, list | int, etc. |
_json_values_union_type(values) |
allOf (object merges) |
BaseModel subclass | Intersection merges fields into a single model |
| Type-guarded object | BaseModel subclass | Guard dispatches to object renderer |
Disallowed patterns
These patterns are errors. The renderer must raise ConversionError instead:
- Unknown IR node kind: the catch-all
case _:inrender()raisesConversionError - Unresolved external refs:
render_refraisesConversionErrorwhenresolvedis None and path doesn't start with# - Silent
Anyfallback: new renderer branches must NOT returnAnywithout an explicit, documented reason. If a construct can be narrowed, it must be.
Required test coverage for new renderer branches
When adding or modifying a renderer function:
- If the branch produces
Any: add a unit test proving it (e.g.test_render_not_stays_any) and document the reason in the "AllowedAnyfallbacks" table above - If the branch produces a narrower type: add a unit test proving the narrow type (e.g.
test_render_oneof_narrows_primitives) - If the branch raises
ConversionError: add a unit test proving the error (e.g.test_render_unknown_ir_kind_raises) - Compliance must not regress: run full compliance after any renderer change
Troubleshooting
pyright errors on src/
The adapter source must pass uv run pyright src/ with zero errors. Common issues:
reveal_type()left in source code — remove before committing- Missing type stubs — add to pyproject.toml
[tool.pyright]config Anyin type annotations — use explicit types orobjectwhere possible
Compliance failures after renderer changes
Run the full compliance suite and compare against baseline. Common causes:
- Changed runtime validation behavior (not just types) — the validator lambda must be functionally identical
- Changed intersection mutation semantics — intersection validators must NOT reassign
v - Type guard check too narrow —
isinstance(v, (list, tuple))not justisinstance(v, list)for array checks
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file valbridge_pydantic-0.4.1.tar.gz.
File metadata
- Download URL: valbridge_pydantic-0.4.1.tar.gz
- Upload date:
- Size: 61.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56cd63959711535e1d8f09d076e879a3099d5f54423561d6b9b3a0c2076f8007
|
|
| MD5 |
822ad464cee1181230d0827f789f3f6b
|
|
| BLAKE2b-256 |
cf2bb32f9155870623a2f826b6cea5cf32788eddfdebf75d45600e8c90444e48
|
Provenance
The following attestation bundles were made for valbridge_pydantic-0.4.1.tar.gz:
Publisher:
release-please.yml on vectorfy-co/valbridge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
valbridge_pydantic-0.4.1.tar.gz -
Subject digest:
56cd63959711535e1d8f09d076e879a3099d5f54423561d6b9b3a0c2076f8007 - Sigstore transparency entry: 1247490718
- Sigstore integration time:
-
Permalink:
vectorfy-co/valbridge@7e6dc6454eeb1071da42491a9b1c0f2ec958ef1d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/vectorfy-co
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@7e6dc6454eeb1071da42491a9b1c0f2ec958ef1d -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file valbridge_pydantic-0.4.1-py3-none-any.whl.
File metadata
- Download URL: valbridge_pydantic-0.4.1-py3-none-any.whl
- Upload date:
- Size: 36.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
70955227986e91a55d7b56c5c52ad4f6f19f47ee27114291b1bcc55363f9f75f
|
|
| MD5 |
100286b09467ed169316c493030f96ac
|
|
| BLAKE2b-256 |
2bde27d6934e5395fc052f4720c50445fc6689c50977c89727c18e0e37681d4c
|
Provenance
The following attestation bundles were made for valbridge_pydantic-0.4.1-py3-none-any.whl:
Publisher:
release-please.yml on vectorfy-co/valbridge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
valbridge_pydantic-0.4.1-py3-none-any.whl -
Subject digest:
70955227986e91a55d7b56c5c52ad4f6f19f47ee27114291b1bcc55363f9f75f - Sigstore transparency entry: 1247490727
- Sigstore integration time:
-
Permalink:
vectorfy-co/valbridge@7e6dc6454eeb1071da42491a9b1c0f2ec958ef1d -
Branch / Tag:
refs/heads/main - Owner: https://github.com/vectorfy-co
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-please.yml@7e6dc6454eeb1071da42491a9b1c0f2ec958ef1d -
Trigger Event:
workflow_run
-
Statement type: