-
Notifications
You must be signed in to change notification settings - Fork 0
Python #11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python #11
Changes from all commits
c548691
efee1b8
0f5df3e
cfe9d88
00f0776
227d8e6
1d42cf2
1d339d2
230897f
a8d9839
1e8ad3b
1874a61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
|
|
||
| from pydantic import ValidationError | ||
|
|
||
| from ._models import A3, VariantRecord | ||
| from ._models import A3, VariantRecord, _BoundsErrors | ||
| from .errors import A3ParseError, A3ValidationError | ||
|
|
||
| _A3_SCHEMA_URI = "https://schema.rtemis.org/a3/v1/schema.json" | ||
|
|
@@ -79,6 +79,9 @@ def create_a3( | |
|
|
||
| try: | ||
| return A3.model_validate(data) | ||
| except _BoundsErrors as exc: | ||
| errors = [{"loc": (), "msg": msg, "type": "value_error"} for msg in exc.messages] | ||
| raise A3ValidationError("\n".join(exc.messages), errors) from exc | ||
|
Comment on lines
+82
to
+84
|
||
| except ValidationError as exc: | ||
| raise A3ValidationError(str(exc), cast(list[dict[str, Any]], exc.errors())) from exc | ||
|
|
||
|
|
@@ -109,33 +112,35 @@ def a3_from_json(text: str) -> A3: | |
| raise A3ParseError(f"invalid JSON: {exc}") from exc | ||
|
|
||
| if not isinstance(data, dict): | ||
| raise A3ParseError("JSON root must be an object") | ||
| raise A3ValidationError( | ||
| "JSON root must be an object", | ||
| [{"loc": (), "msg": "JSON root must be an object", "type": "value_error", "input": data}], | ||
| ) | ||
| envelope_errors: list[dict[str, Any]] = [] | ||
| schema_val = data.get("$schema") | ||
| if schema_val is None: | ||
| raise A3ParseError("missing required field '$schema'") | ||
| if schema_val != _A3_SCHEMA_URI: | ||
| raise A3ParseError( | ||
| f"'$schema' must be '{_A3_SCHEMA_URI}', got '{schema_val}'" | ||
| ) | ||
| envelope_errors.append({"loc": ("$schema",), "msg": "missing required field '$schema'", "type": "missing", "input": data}) | ||
| elif schema_val != _A3_SCHEMA_URI: | ||
| envelope_errors.append({"loc": ("$schema",), "msg": f"'$schema' must be '{_A3_SCHEMA_URI}', got '{schema_val}'", "type": "value_error", "input": schema_val}) | ||
| version_val = data.get("a3_version") | ||
| if version_val is None: | ||
| raise A3ParseError("missing required field 'a3_version'") | ||
| if version_val != _A3_VERSION: | ||
| raise A3ParseError( | ||
| f"'a3_version' must be '{_A3_VERSION}', got '{version_val}'" | ||
| envelope_errors.append({"loc": ("a3_version",), "msg": "missing required field 'a3_version'", "type": "missing", "input": data}) | ||
| elif version_val != _A3_VERSION: | ||
| envelope_errors.append({"loc": ("a3_version",), "msg": f"'a3_version' must be '{_A3_VERSION}', got '{version_val}'", "type": "value_error", "input": version_val}) | ||
| if envelope_errors: | ||
| raise A3ValidationError( | ||
| "; ".join(e["msg"] for e in envelope_errors), | ||
| envelope_errors, | ||
| ) | ||
| if "sequence" not in data: | ||
| raise A3ParseError("missing required field 'sequence'") | ||
| if "annotations" not in data: | ||
| raise A3ParseError("missing required field 'annotations'") | ||
| if "metadata" not in data: | ||
| raise A3ParseError("missing required field 'metadata'") | ||
| # Strip envelope keys before passing to the data model | ||
| for key in _ENVELOPE_KEYS: | ||
| data.pop(key, None) | ||
|
|
||
| try: | ||
| return A3.model_validate(data) | ||
| except _BoundsErrors as exc: | ||
| errors = [{"loc": (), "msg": msg, "type": "value_error"} for msg in exc.messages] | ||
| raise A3ValidationError("\n".join(exc.messages), errors) from exc | ||
|
Comment on lines
+141
to
+143
|
||
| except ValidationError as exc: | ||
| raise A3ValidationError(str(exc), cast(list[dict[str, Any]], exc.errors())) from exc | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,13 +17,22 @@ class A3ValidationError(Exception): | |||||||||||||||||||||||
| message : str | ||||||||||||||||||||||||
| Human-readable summary. | ||||||||||||||||||||||||
| errors : list[dict[str, Any]] | ||||||||||||||||||||||||
| Structured error list (from Pydantic's ``ValidationError.errors()``). | ||||||||||||||||||||||||
| Structured error list. Each entry has at minimum ``loc`` (tuple of | ||||||||||||||||||||||||
| field path components), ``msg`` (human-readable message), and | ||||||||||||||||||||||||
| ``type`` (error kind string). Pydantic structural errors follow | ||||||||||||||||||||||||
| Pydantic's native format; bounds and envelope errors follow the same | ||||||||||||||||||||||||
| shape for consistency. | ||||||||||||||||||||||||
|
Comment on lines
+20
to
+24
|
||||||||||||||||||||||||
| Structured error list. Each entry has at minimum ``loc`` (tuple of | |
| field path components), ``msg`` (human-readable message), and | |
| ``type`` (error kind string). Pydantic structural errors follow | |
| Pydantic's native format; bounds and envelope errors follow the same | |
| shape for consistency. | |
| Structured error list. Each entry has at minimum ``loc`` (a tuple of | |
| field path components, or ``()`` for root-level/non-field-specific | |
| violations), ``msg`` (human-readable message), and ``type`` (error | |
| kind string). Pydantic structural errors follow Pydantic's native | |
| format; contextual errors such as bounds or envelope violations may | |
| use an empty ``loc`` when no specific field path applies. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check for boolean values here is redundant. The
RegionEntrymodel defines theindexfield aslist[tuple[Position, Position]], and thePositiontype alias already includes aBeforeValidator(_reject_bool)that handles this check. Since_normalize_rangesis abeforevalidator, Pydantic will subsequently validate its output against the field's type annotation, making this explicit check unnecessary. Relying on thePositiontype for this validation will make the code more concise and less repetitive.