From b2d16c7ce7c4acbbc154e4768ba21bc1d505c0c6 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 11:19:22 +1200 Subject: [PATCH 01/17] codegen(python): drop ReflectapiOption, use model_fields_set for partial fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `reflectapi::Option` is a three-state protocol type: `Some(value)` / explicit `None` / `Undefined` (key absent on the wire). TypeScript clients express that natively (`field?: T | null`); Rust clients use the `reflectapi::Option` enum. The Python client previously shipped a hand-rolled `ReflectapiOption[T]` wrapper class to carry the same distinction — but Pydantic already tracks "was this field on the wire?" via `__pydantic_fields_set__`. The wrapper was duplicating Pydantic's own bookkeeping and forcing users to learn a parallel `.unwrap` / `.is_undefined` API. This rip-out replaces the wrapper with a `ReflectapiPartialModel` mixin (one `@model_serializer` that filters by `model_fields_set`). Generated classes that contain at least one `reflectapi::Option` field inherit from it; field types collapse to plain `T | None`. API for callers: # Field omitted on the wire if you don't pass it Item(name="rex").model_dump_json() == '{"name":"rex"}' # Field is `null` on the wire if you pass `None` explicitly Item(name="rex", kind=None).model_dump_json() == '{"name":"rex","kind":null}' # Was this field on the wire? "kind" in item.model_fields_set # exact analogue of TS # `obj.kind !== undefined` Runtime changes - New: `reflectapi_runtime.partial.ReflectapiPartialModel`. Exposes one `@model_serializer(mode="wrap")` that drops keys not in `model_fields_set`, honouring `by_alias=True`. - Deleted: `reflectapi_runtime.option` (the entire `ReflectapiOption` / `Undefined` / `some` / `none` / `undefined` / `serialize_option_dict` surface). - `client._serialize_request_body` and `streaming` no longer need the option-dict dance — they just call `model.model_dump_json(by_alias=True)` and trust the model. Codegen changes - `reflectapi::Option` now maps to `T | None` (was `ReflectapiOption[T]`); default is `None`. - `Field.is_partial` and `DataClass.is_partial` track whether any field originated from `reflectapi::Option`; the renderer picks `ReflectapiPartialModel` vs `BaseModel` and adds `validate_assignment=True` to the ConfigDict on partial classes so post-construction assignments land on the wire. - New helper `schema_has_partial_field` triggers the `ReflectapiPartialModel` runtime import. Tests - New: `tests/test_partial.py` (21 tests pinning the wire-format contract — absent vs null vs value, nested partial, container of models, JSON round-trip, aliases, defaults). - `tests/test_option.py` deleted. - `tests/test_edge_cases.py` and `tests/test_enhanced_features.py` stripped of dead `ReflectapiOption` test classes; the integration test was rewritten against `ReflectapiPartialModel`. - Total runtime suite: 347 pass, 0 fail. - Demo client regenerated; workspace tests + clippy clean. BREAKING CHANGE: This is a 0.18.x change. Code importing `ReflectapiOption`, `Undefined`, `some`, `none`, `serialize_option_dict` from `reflectapi_runtime` will need to migrate to plain `T | None` fields and query `model_fields_set` for presence. --- .../clients/python/api_client/__init__.py | 4 +- .../clients/python/api_client/_client.py | 4 +- .../clients/python/api_client/_rebuild.py | 10 + .../clients/python/api_client/generated.py | 13 + .../python/api_client/myapi/__init__.py | 2 +- .../python/api_client/myapi/model/__init__.py | 2 +- .../api_client/myapi/model/input/__init__.py | 2 +- .../api_client/myapi/model/output/__init__.py | 2 +- .../python/api_client/myapi/proto/__init__.py | 12 +- .../python/api_client/reflectapi/__init__.py | 100 +++++ .../src/reflectapi_runtime/__init__.py | 18 +- .../src/reflectapi_runtime/client.py | 138 ++---- .../hypothesis_strategies.py | 26 -- .../src/reflectapi_runtime/option.py | 296 ------------- .../src/reflectapi_runtime/partial.py | 115 +++++ .../src/reflectapi_runtime/streaming.py | 23 +- .../tests/test_edge_cases.py | 164 +------ .../tests/test_enhanced_features.py | 69 ++- .../tests/test_option.py | 411 ------------------ .../tests/test_partial.py | 233 ++++++++++ reflectapi/src/codegen/python.rs | 131 +++++- 21 files changed, 669 insertions(+), 1106 deletions(-) create mode 100644 reflectapi-demo/clients/python/api_client/reflectapi/__init__.py delete mode 100644 reflectapi-python-runtime/src/reflectapi_runtime/option.py create mode 100644 reflectapi-python-runtime/src/reflectapi_runtime/partial.py delete mode 100644 reflectapi-python-runtime/tests/test_option.py create mode 100644 reflectapi-python-runtime/tests/test_partial.py diff --git a/reflectapi-demo/clients/python/api_client/__init__.py b/reflectapi-demo/clients/python/api_client/__init__.py index 673ef347..fd260686 100644 --- a/reflectapi-demo/clients/python/api_client/__init__.py +++ b/reflectapi-demo/clients/python/api_client/__init__.py @@ -10,8 +10,10 @@ from . import myapi +from . import reflectapi + from ._rebuild import rebuild_models as _rebuild_models _rebuild_models() -__all__ = ["AsyncClient", "Client", "myapi"] +__all__ = ["AsyncClient", "Client", "myapi", "reflectapi"] diff --git a/reflectapi-demo/clients/python/api_client/_client.py b/reflectapi-demo/clients/python/api_client/_client.py index 6abc7b62..1e3460a2 100644 --- a/reflectapi-demo/clients/python/api_client/_client.py +++ b/reflectapi-demo/clients/python/api_client/_client.py @@ -29,7 +29,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiOption +from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, serialize_externally_tagged as _serialize_externally_tagged, @@ -44,6 +44,8 @@ from . import myapi +from . import reflectapi + from ._rebuild import rebuild_models as _rebuild_models _rebuild_models() diff --git a/reflectapi-demo/clients/python/api_client/_rebuild.py b/reflectapi-demo/clients/python/api_client/_rebuild.py index 121b5331..a53eacec 100644 --- a/reflectapi-demo/clients/python/api_client/_rebuild.py +++ b/reflectapi-demo/clients/python/api_client/_rebuild.py @@ -56,6 +56,15 @@ MyapiProtoValidationErrorValidationAVariant, ) +from . import reflectapi + +from .reflectapi import ( + ReflectapiOption, + ReflectapiOptionNone, + ReflectapiOptionSome, + ReflectapiOptionUndefined, +) + def rebuild_models() -> None: myapi.myapi = myapi @@ -63,6 +72,7 @@ def rebuild_models() -> None: myapi.model.input.myapi = myapi myapi.model.output.myapi = myapi myapi.proto.myapi = myapi + reflectapi.reflectapi = reflectapi for _model in [ MyapiHealthCheckFail, MyapiModelBehavior, diff --git a/reflectapi-demo/clients/python/api_client/generated.py b/reflectapi-demo/clients/python/api_client/generated.py index 6a9f72e2..749061b1 100644 --- a/reflectapi-demo/clients/python/api_client/generated.py +++ b/reflectapi-demo/clients/python/api_client/generated.py @@ -60,6 +60,15 @@ MyapiProtoValidationErrorValidationAVariant, ) +from . import reflectapi + +from .reflectapi import ( + ReflectapiOption, + ReflectapiOptionNone, + ReflectapiOptionSome, + ReflectapiOptionUndefined, +) + from ._rebuild import rebuild_models as _rebuild_models _rebuild_models() @@ -95,4 +104,8 @@ "MyapiProtoValidationA", "MyapiProtoValidationError", "MyapiProtoValidationErrorValidationAVariant", + "ReflectapiOption", + "ReflectapiOptionNone", + "ReflectapiOptionSome", + "ReflectapiOptionUndefined", ] diff --git a/reflectapi-demo/clients/python/api_client/myapi/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/__init__.py index 4de8151c..decc0a23 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/__init__.py @@ -29,7 +29,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiOption +from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, serialize_externally_tagged as _serialize_externally_tagged, diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py index a89200d1..1c328c31 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py @@ -29,7 +29,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiOption +from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, serialize_externally_tagged as _serialize_externally_tagged, diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py index 9afb6c98..e1bd042e 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py @@ -29,7 +29,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiOption +from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, serialize_externally_tagged as _serialize_externally_tagged, diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py index 677a0001..7226c6f4 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py @@ -29,7 +29,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiOption +from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, serialize_externally_tagged as _serialize_externally_tagged, diff --git a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py index 95325dba..5d025583 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py @@ -29,7 +29,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiOption +from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, serialize_externally_tagged as _serialize_externally_tagged, @@ -81,17 +81,19 @@ class MyapiProtoPetsRemoveRequest(BaseModel): name: str = Field(description="identity") -class MyapiProtoPetsUpdateRequest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) +class MyapiProtoPetsUpdateRequest(ReflectapiPartialModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, validate_assignment=True + ) name: str = Field(description="identity") kind: myapi.model.Kind | None = Field( default=None, description="kind of pet, non nullable in the model" ) - age: ReflectapiOption[int] = Field( + age: int | None = Field( default=None, description="age of the pet, nullable in the model" ) - behaviors: ReflectapiOption[list[myapi.model.Behavior]] = Field( + behaviors: list[myapi.model.Behavior] | None = Field( default=None, description="behaviors of the pet, nullable in the model" ) diff --git a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py new file mode 100644 index 00000000..5326c6c0 --- /dev/null +++ b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py @@ -0,0 +1,100 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Demo application +This is a demo application +""" + +from __future__ import annotations + + +# Standard library imports +import warnings +from collections.abc import AsyncIterator, Iterator +from datetime import datetime +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import ( + BaseModel, + ConfigDict, + Field, + RootModel, + model_serializer, + model_validator, +) + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiPartialModel +from reflectapi_runtime import ( + parse_externally_tagged as _parse_externally_tagged, + serialize_externally_tagged as _serialize_externally_tagged, +) + + +# Type variables for generic types + + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class ReflectapiOptionUndefined(BaseModel): + """The value is missing, i.e. undefined in JavaScript""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + +class ReflectapiOptionNone(BaseModel): + """The value is provided but set to none, i.e. null in JavaScript""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + +class ReflectapiOptionSome(BaseModel): + """The value is provided and set to some value""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + value: T | None = None + + +ReflectapiOption = Union[ + ReflectapiOptionUndefined, ReflectapiOptionNone, ReflectapiOptionSome +] +"""Undefinable Option type""" + + +# Public aliases for this module +Option = ReflectapiOption +OptionNone = ReflectapiOptionNone +OptionSome = ReflectapiOptionSome +OptionUndefined = ReflectapiOptionUndefined + +try: + from .._rebuild import rebuild_models as _rebuild_models + + _rebuild_models() +except Exception: + pass + +__all__ = [ + "Option", + "OptionNone", + "OptionSome", + "OptionUndefined", + "ReflectapiOption", + "ReflectapiOptionNone", + "ReflectapiOptionSome", + "ReflectapiOptionUndefined", +] diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py b/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py index 6dbe43c3..a0e82948 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py @@ -36,15 +36,7 @@ strategy_for_type, ) from .middleware import AsyncMiddleware -from .option import ( - Option, - ReflectapiOption, - Undefined, - none, - serialize_option_dict, - some, - undefined, -) +from .partial import ReflectapiPartialModel from .response import ApiResponse, TransportMetadata from .serde import parse_externally_tagged, serialize_externally_tagged from .streaming import AsyncStreamingClient, StreamingResponse @@ -94,24 +86,18 @@ "AsyncMiddleware", "MockClient", "NetworkError", - "Option", "ReflectapiEmpty", "ReflectapiInfallible", - "ReflectapiOption", + "ReflectapiPartialModel", "parse_externally_tagged", "serialize_externally_tagged", "StreamingResponse", "TestClientMixin", "TimeoutError", "TransportMetadata", - "Undefined", "ValidationError", "api_model_strategy", "enhanced_strategy_for_type", - "none", - "serialize_option_dict", - "some", "strategy_for_pydantic_model", "strategy_for_type", - "undefined", ] diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/client.py b/reflectapi-python-runtime/src/reflectapi_runtime/client.py index 42904e80..323975bd 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/client.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/client.py @@ -21,7 +21,6 @@ SyncMiddleware, SyncMiddlewareChain, ) -from .option import serialize_option_dict from .response import ApiResponse, TransportMetadata from .sse import aparse_sse, parse_sse from .transport import AsyncClient, Client, Request, Response @@ -252,60 +251,24 @@ def _validate_request_params( def _serialize_request_body( self, json_model: BaseModel | int | float | str | bool | list | dict ) -> tuple[bytes, dict[str, str]]: - """Serialize request body from Pydantic model or primitive type.""" - from .option import ReflectapiOption - + """Serialize the request body. + + Generated models that contain partial (``reflectapi::Option``) + fields inherit from :class:`ReflectapiPartialModel`, whose + ``@model_serializer`` already omits keys not in + ``model_fields_set``. Plain Pydantic models serialise normally. + Either way, ``model_dump_json(by_alias=True)`` is the right call — + the model decides what goes on the wire, not the transport. + """ # Handle primitive types (for untagged unions) - if not hasattr(json_model, "model_dump"): + if not hasattr(json_model, "model_dump_json"): content = json.dumps( json_model, default=_json_serializer, separators=(",", ":") ).encode("utf-8") - headers = {"Content-Type": "application/json"} - return content, headers - - # Check if model has any ReflectapiOption fields that need special handling - raw_data = json_model.model_dump(exclude_none=False) - - # Handle case where RootModel serializes to primitive value (e.g., strings for unit variants) - if not isinstance(raw_data, dict): - # For primitive values, use Pydantic's built-in JSON serialization - content = json_model.model_dump_json( - exclude_none=True, by_alias=True - ).encode("utf-8") - headers = {"Content-Type": "application/json"} - return content, headers - - has_reflectapi_options = any( - isinstance(field_value, ReflectapiOption) - for field_value in raw_data.values() - ) - - if has_reflectapi_options: - # Process each field to handle ReflectapiOption properly - processed_fields = {} - for field_name, field_value in raw_data.items(): - if isinstance(field_value, ReflectapiOption): - if not field_value.is_undefined: - # Include the unwrapped value (including None for explicit null) - processed_fields[field_name] = field_value._value - # Skip undefined fields entirely - don't include them at all - else: - # Include all other fields that aren't None (unless they're meaningful None values) - if field_value is not None: - processed_fields[field_name] = field_value - - # Use json serialization with datetime handler for proper serialization - content = json.dumps( - processed_fields, default=_json_serializer, separators=(",", ":") - ).encode("utf-8") - else: - # Use Pydantic's built-in JSON serialization with exclude_none and by_alias for proper handling - content = json_model.model_dump_json( - exclude_none=True, by_alias=True - ).encode("utf-8") + return content, {"Content-Type": "application/json"} - headers = {"Content-Type": "application/json"} - return content, headers + content = json_model.model_dump_json(by_alias=True).encode("utf-8") + return content, {"Content-Type": "application/json"} def _build_headers( self, base_headers: dict[str, str], headers_model: BaseModel | None @@ -334,12 +297,8 @@ def _build_client_request( content, base_headers = self._serialize_request_body(json_model) headers = self._build_headers(base_headers, headers_model) elif json_data is not None: - if isinstance(json_data, dict): - processed_json_data = serialize_option_dict(json_data) - else: - processed_json_data = json_data content = json.dumps( - processed_json_data, + json_data, default=_json_serializer, separators=(",", ":"), ).encode("utf-8") @@ -844,61 +803,24 @@ def _validate_request_params( def _serialize_request_body( self, json_model: BaseModel | int | float | str | bool | list | dict ) -> tuple[bytes, dict[str, str]]: - """Serialize request body from Pydantic model or primitive type.""" - from .option import ReflectapiOption - + """Serialize the request body. + + Generated models that contain partial (``reflectapi::Option``) + fields inherit from :class:`ReflectapiPartialModel`, whose + ``@model_serializer`` already omits keys not in + ``model_fields_set``. Plain Pydantic models serialise normally. + Either way, ``model_dump_json(by_alias=True)`` is the right call — + the model decides what goes on the wire, not the transport. + """ # Handle primitive types (for untagged unions) - if not hasattr(json_model, "model_dump"): + if not hasattr(json_model, "model_dump_json"): content = json.dumps( json_model, default=_json_serializer, separators=(",", ":") ).encode("utf-8") - headers = {"Content-Type": "application/json"} - return content, headers - - # Check if model has any ReflectapiOption fields that need special handling - raw_data = json_model.model_dump(exclude_none=False) + return content, {"Content-Type": "application/json"} - # Handle case where RootModel serializes to primitive value (e.g., strings for unit variants) - if not isinstance(raw_data, dict): - # For primitive values, use Pydantic's built-in JSON serialization - content = json_model.model_dump_json( - exclude_none=True, by_alias=True - ).encode("utf-8") - headers = {"Content-Type": "application/json"} - return content, headers - - has_reflectapi_options = any( - isinstance(field_value, ReflectapiOption) - for field_value in raw_data.values() - ) - - if has_reflectapi_options: - # Process each field to handle ReflectapiOption properly - processed_fields = {} - for field_name, field_value in raw_data.items(): - if isinstance(field_value, ReflectapiOption): - if not field_value.is_undefined: - # Include the unwrapped value (including None for explicit null) - processed_fields[field_name] = field_value._value - # Skip undefined fields entirely - don't include them at all - else: - # Include all other fields that aren't None (unless they're meaningful None values) - if field_value is not None: - processed_fields[field_name] = field_value - - # Use json serialization with datetime handler for proper serialization - content = json.dumps( - processed_fields, default=_json_serializer, separators=(",", ":") - ).encode("utf-8") - else: - # Use Pydantic's built-in JSON serialization with exclude_none and by_alias for proper handling - content = json_model.model_dump_json( - exclude_none=True, by_alias=True - ).encode("utf-8") - - headers = {"Content-Type": "application/json"} - - return content, headers + content = json_model.model_dump_json(by_alias=True).encode("utf-8") + return content, {"Content-Type": "application/json"} def _build_headers( self, base_headers: dict[str, str], headers_model: BaseModel | None @@ -927,12 +849,8 @@ def _build_client_request( content, base_headers = self._serialize_request_body(json_model) headers = self._build_headers(base_headers, headers_model) elif json_data is not None: - if isinstance(json_data, dict): - processed_json_data = serialize_option_dict(json_data) - else: - processed_json_data = json_data content = json.dumps( - processed_json_data, + json_data, default=_json_serializer, separators=(",", ":"), ).encode("utf-8") diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/hypothesis_strategies.py b/reflectapi-python-runtime/src/reflectapi_runtime/hypothesis_strategies.py index 79ffe44f..a8750d85 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/hypothesis_strategies.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/hypothesis_strategies.py @@ -20,25 +20,8 @@ from pydantic import BaseModel -from .option import ReflectapiOption, Undefined - if HAS_HYPOTHESIS: - def strategy_for_option(inner_strategy: st.SearchStrategy) -> st.SearchStrategy: - """Create a strategy for ReflectapiOption types. - - Args: - inner_strategy: Strategy for the inner type T in Option. - - Returns: - Strategy that produces ReflectapiOption instances with undefined, None, or values. - """ - return st.one_of( - st.just(ReflectapiOption(Undefined)), # Undefined case - st.just(ReflectapiOption(None)), # None case - inner_strategy.map(ReflectapiOption), # Some(value) case - ) - def strategy_for_type(type_hint: type) -> st.SearchStrategy: """Generate a Hypothesis strategy for a given type hint. @@ -69,15 +52,6 @@ def strategy_for_type(type_hint: type) -> st.SearchStrategy: elif type_hint is uuid.UUID: return st.uuids() - # Handle ReflectapiOption specifically - if ( - hasattr(type_hint, "__origin__") - and type_hint.__origin__ is ReflectapiOption - ): - inner_type = get_args(type_hint)[0] if get_args(type_hint) else Any - inner_strategy = strategy_for_type(inner_type) - return strategy_for_option(inner_strategy) - # Handle generic types origin = get_origin(type_hint) args = get_args(type_hint) diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/option.py b/reflectapi-python-runtime/src/reflectapi_runtime/option.py deleted file mode 100644 index bd16ec16..00000000 --- a/reflectapi-python-runtime/src/reflectapi_runtime/option.py +++ /dev/null @@ -1,296 +0,0 @@ -"""Option type handling for reflectapi code generation. - -This module provides proper handling of Rust's Option types in Python, -distinguishing between undefined, null, and actual values. -""" - -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Generic, TypeVar - -if TYPE_CHECKING: - from pydantic import GetCoreSchemaHandler - -from pydantic_core import core_schema - -T = TypeVar("T") - - -class _UndefinedType: - """Sentinel type for undefined values in reflectapi Option types. - - This represents the state where a field was not provided at all, - as opposed to being explicitly set to None/null. - """ - - def __repr__(self) -> str: - return "Undefined" - - def __str__(self) -> str: - return "Undefined" - - def __bool__(self) -> bool: - return False - - def __eq__(self, other: Any) -> bool: - return isinstance(other, _UndefinedType) - - def __hash__(self) -> int: - return hash("_UndefinedType") - - -# Global singleton instance -Undefined = _UndefinedType() - - -class ReflectapiOption(Generic[T]): - """Proper representation of Rust's Option type in Python. - - This class encapsulates the three possible states: - - Undefined: Field was not provided - - None: Field was explicitly set to null - - Some(value): Field has an actual value - - Example: - ```python - # Field not provided - option = ReflectapiOption() # or ReflectapiOption(Undefined) - - # Field explicitly set to null - option = ReflectapiOption(None) - - # Field has a value - option = ReflectapiOption(42) - ``` - """ - - def __init__(self, value: T | None | _UndefinedType = Undefined): - self._value = value - - @classmethod - def __get_pydantic_core_schema__( - cls, source_type: Any, handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: - """Generate Pydantic V2 core schema for ReflectapiOption.""" - from typing import get_args, get_origin - - # Extract the generic type argument if available - origin = get_origin(source_type) - args = get_args(source_type) - - def validate_option(value: Any) -> ReflectapiOption[Any]: - if isinstance(value, cls): - return value - return cls(value) - - def serialize_option(option_value: ReflectapiOption[Any]) -> Any: - """Serialize ReflectapiOption handling all three states correctly.""" - if isinstance(option_value, cls): - if option_value.is_undefined: - # Return None for undefined to avoid pydantic undefined serialization issues - return None - # Return the actual value (including None for explicit null) - return option_value._value - # Fallback for non-ReflectapiOption values - return option_value - - if origin is cls and args: - # We have ReflectapiOption[SomeType] - inner_type = args[0] - inner_schema = handler(inner_type) - - return core_schema.no_info_plain_validator_function( - validate_option, - serialization=core_schema.plain_serializer_function_ser_schema( - serialize_option, - return_schema=core_schema.union_schema( - [ - inner_schema, - core_schema.none_schema(), - ] - ), - when_used="json", - ), - ) - else: - # Fallback for untyped ReflectapiOption - return core_schema.no_info_plain_validator_function(validate_option) - - @property - def is_undefined(self) -> bool: - """Check if the option is undefined (field not provided).""" - return self._value is Undefined - - @property - def is_none(self) -> bool: - """Check if the option is explicitly None/null.""" - return self._value is None - - @property - def is_some(self) -> bool: - """Check if the option has a value (not undefined and not None).""" - return self._value is not Undefined and self._value is not None - - @property - def value(self) -> T | None: - """Get the wrapped value. - - Returns: - The wrapped value, or None if undefined or null. - - Raises: - ValueError: If trying to access value when undefined. - """ - if self.is_undefined: - raise ValueError("Cannot access value of undefined option") - return self._value - - def unwrap(self) -> T: - """Unwrap the option, returning the value or raising an error. - - Returns: - The wrapped value. - - Raises: - ValueError: If the option is undefined or None. - """ - if self.is_undefined: - raise ValueError("Cannot unwrap undefined option") - if self.is_none: - raise ValueError("Cannot unwrap None option") - return self._value - - def unwrap_or(self, default: T) -> T: - """Unwrap the option or return a default value. - - Args: - default: Default value to return if undefined or None. - - Returns: - The wrapped value or the default. - """ - if self.is_some: - return self._value - return default - - def map(self, func: callable) -> ReflectapiOption: - """Apply a function to the wrapped value if it exists. - - Args: - func: Function to apply to the value. - - Returns: - New ReflectapiOption with the result, or unchanged if undefined/None. - """ - if self.is_some: - return ReflectapiOption(func(self._value)) - return ReflectapiOption(self._value) - - def filter(self, predicate: callable) -> ReflectapiOption: - """Filter the option based on a predicate. - - Args: - predicate: Function that returns True to keep the value. - - Returns: - The option if predicate returns True, otherwise undefined. - """ - if self.is_some and predicate(self._value): - return self - return ReflectapiOption(Undefined) - - def __eq__(self, other: Any) -> bool: - if isinstance(other, ReflectapiOption): - return self._value == other._value - return self._value == other - - def __hash__(self) -> int: - return hash(self._value) - - def __repr__(self) -> str: - if self.is_undefined: - return "ReflectapiOption(Undefined)" - elif self.is_none: - return "ReflectapiOption(None)" - else: - return f"ReflectapiOption({self._value!r})" - - def __str__(self) -> str: - if self.is_undefined: - return "Undefined" - elif self.is_none: - return "None" - else: - return str(self._value) - - def __bool__(self) -> bool: - """Return True if the option has a truthy value.""" - return self.is_some and bool(self._value) - - -# Type alias for more concise usage -Option = ReflectapiOption - - -def some(value: T) -> ReflectapiOption[T]: - """Create an Option with a value.""" - return ReflectapiOption(value) - - -def none() -> ReflectapiOption[None]: - """Create an Option with None.""" - return ReflectapiOption(None) - - -def undefined() -> ReflectapiOption: - """Create an undefined Option.""" - return ReflectapiOption(Undefined) - - -# Utility functions for serialization -def serialize_option_dict(data: dict) -> dict: - """Serialize a dictionary, excluding undefined option fields. - - This is used in client serialization to properly handle Option types - by excluding undefined fields from the JSON payload. - - Args: - data: Dictionary that may contain ReflectapiOption values. - - Returns: - Dictionary with undefined options excluded and others unwrapped. - """ - result = {} - - for key, value in data.items(): - if isinstance(value, ReflectapiOption): - if not value.is_undefined: - # Include None values but not undefined ones - result[key] = value._value - elif isinstance(value, dict): - # Recursively handle nested dictionaries - result[key] = serialize_option_dict(value) - elif isinstance(value, list): - # Handle lists that might contain options or nested structures - processed_items = [] - for item in value: - if isinstance(item, ReflectapiOption): - if not item.is_undefined: - processed_items.append(item._value) - elif isinstance(item, dict): - # Recursively handle dictionaries within lists - processed_items.append(serialize_option_dict(item)) - else: - processed_items.append(item) - result[key] = processed_items - else: - result[key] = value - - return result - - -def is_undefined(value: Any) -> bool: - """Check if a value is undefined.""" - if isinstance(value, ReflectapiOption): - return value.is_undefined - return value is Undefined diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/partial.py b/reflectapi-python-runtime/src/reflectapi_runtime/partial.py new file mode 100644 index 00000000..edd61701 --- /dev/null +++ b/reflectapi-python-runtime/src/reflectapi_runtime/partial.py @@ -0,0 +1,115 @@ +"""Partial-model base class for reflectapi-generated Pydantic models. + +reflectapi schemas distinguish three states for nullable fields declared +as ``reflectapi::Option``: ``Some(value)``, explicit ``null``, and +"key absent on the wire" (``Undefined``). TypeScript clients express +this natively (``field?: T | null``); Rust clients use the +``reflectapi::Option`` enum. Python clients used to ship a custom +``ReflectapiOption[T]`` wrapper class to surface the same distinction. + +The wrapper duplicated information Pydantic already tracks via +``model_fields_set`` and forced users to learn a parallel +``.unwrap`` / ``.is_undefined`` API. This module replaces it with a +``BaseModel`` mixin that: + +- leaves field types as plain ``T | None`` (so Pydantic validates them + exactly as it would any other nullable field), +- relies on ``model_fields_set`` — Pydantic's built-in record of which + fields were *actually provided* during deserialise / construction — + as the source of truth for "was this on the wire?", +- emits a wire payload that omits keys not in ``model_fields_set``, + preserving the absent-vs-null distinction round-trip. + +Codegen marks every generated class with at least one +``reflectapi::Option`` field as inheriting from +:class:`ReflectapiPartialModel` instead of :class:`pydantic.BaseModel`. +""" + +from __future__ import annotations + +from typing import Any + +from pydantic import BaseModel, model_serializer + + +class ReflectapiPartialModel(BaseModel): + """Base class for reflectapi models with partial (three-state) fields. + + Wire-format guarantee: a field that was never explicitly set on the + instance is omitted from the serialised output entirely. A field set + to ``None`` is emitted as ``null``. A field set to a value is emitted + as that value. This mirrors what TypeScript clients produce for + ``field?: T | null`` and what Rust clients produce for + ``reflectapi::Option``. + + Usage on the read path: + + .. code-block:: python + + item = Item.model_validate({"name": "x"}) # snapshot key absent + "snapshot" in item.model_fields_set # False — was absent + + item = Item.model_validate({"name": "x", "snapshot": None}) + "snapshot" in item.model_fields_set # True — explicit null + item.snapshot is None # True + + Usage on the write path: + + .. code-block:: python + + Item(name="x").model_dump_json() + # '{"name":"x"}' — snapshot omitted because it wasn't set + + Item(name="x", snapshot=None).model_dump_json() + # '{"name":"x","snapshot":null}' + """ + + # NOTE on ``model_fields_set`` and post-construction assignment: + # Pydantic populates ``model_fields_set`` during construction + # (kwargs) and deserialise (``model_validate``). Subsequent + # attribute writes do **not** add to that set unless the model's + # ``model_config`` enables ``validate_assignment=True``. The + # generated client code emits ``ConfigDict(extra="ignore", + # populate_by_name=True, validate_assignment=True)`` on every + # partial class for this reason, so users can also do + # ``m.snapshot = None`` after construction and have it land on the + # wire. + + @model_serializer(mode="wrap") + def _serialize_partial( + self, handler: Any, info: Any | None = None + ) -> dict[str, Any]: + """Drop fields the caller never explicitly set. + + Pydantic populates ``model_fields_set`` with the *field names* + of every field that was either present in the input dict during + ``model_validate`` *or* passed as a keyword to ``__init__``. + Defaults populated by Pydantic itself are excluded — which is + exactly the "was this on the wire?" signal we need. + + Two complications the implementation has to handle: + + 1. When ``by_alias=True`` is in effect, the handler returns a + dict keyed by the field's serialization alias, not by its + Python attribute name. ``model_fields_set`` always holds the + Python name, so we expand it into the set of names that + could plausibly appear in ``data`` (Python name + alias). + + 2. RootModel and friends may return non-dict data (e.g. a bare + string for a unit-variant enum). In that case there's nothing + to filter; just pass it through. + """ + data = handler(self) + if not isinstance(data, dict): + return data + emit_keys: set[str] = set() + model_fields = type(self).model_fields + for name in self.model_fields_set: + emit_keys.add(name) + field_info = model_fields.get(name) + if field_info is None: + continue + alias = field_info.serialization_alias or field_info.alias + if alias: + emit_keys.add(alias) + return {key: value for key, value in data.items() if key in emit_keys} diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py b/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py index 0f1e64d4..c226e0f1 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py @@ -11,7 +11,6 @@ from .auth import AuthHandler from .exceptions import ApplicationError, NetworkError, TimeoutError from .middleware import AsyncMiddleware, AsyncMiddlewareChain -from .option import serialize_option_dict from .response import TransportMetadata if TYPE_CHECKING: @@ -237,26 +236,14 @@ async def stream_request( request_headers = headers.copy() if headers else {} - # Serialize Pydantic model + # Serialize Pydantic model. ReflectapiPartialModel emits its own + # @model_serializer that omits keys not in `model_fields_set`, so + # plain `model_dump_json(by_alias=True)` produces the right wire + # shape for both partial and plain generated models. if json_model is not None: - # Use Pydantic with improved ReflectapiOption serialization - model_dict = json_model.model_dump(exclude_none=False) # Keep explicit None - - # Filter out Undefined values (ReflectapiOption serializer returns Undefined sentinel) - from .option import Undefined - - final_dict = { - k: v - for k, v in model_dict.items() - if not (hasattr(v, "__class__") and v is Undefined) - } - - import json - - content = json.dumps(final_dict, separators=(",", ":")).encode("utf-8") + content = json_model.model_dump_json(by_alias=True).encode("utf-8") request_headers["Content-Type"] = "application/json" - # Build request with raw content request = self._client.build_request( method=method, url=url, diff --git a/reflectapi-python-runtime/tests/test_edge_cases.py b/reflectapi-python-runtime/tests/test_edge_cases.py index d2171626..a8864cf0 100644 --- a/reflectapi-python-runtime/tests/test_edge_cases.py +++ b/reflectapi-python-runtime/tests/test_edge_cases.py @@ -19,10 +19,8 @@ BearerTokenAuth, ClientBase, NetworkError, - ReflectapiOption, TimeoutError, TransportMetadata, - Undefined, ) from reflectapi_runtime import ( ValidationError as ReflectApiValidationError, @@ -34,7 +32,7 @@ class EdgeCaseTestModel(BaseModel): name: str value: int - optional_field: ReflectapiOption[str] = ReflectapiOption() + optional_field: str | None = None class TestClientEdgeCases: @@ -142,8 +140,12 @@ def test_request_with_circular_reference_in_json_data(self, mock_send): data["self"] = data client = ClientBase("https://api.example.com") - # Should raise RecursionError for circular references - with pytest.raises(RecursionError): + # `json.dumps` detects circular references and raises ValueError. + # (The previous implementation hit Python's recursion limit + # because it pre-traversed the dict; the current path delegates + # directly to the JSON encoder, which surfaces the error sooner + # with a clearer message.) + with pytest.raises(ValueError, match="Circular reference"): client._make_request("/test", json_data=data) @@ -190,81 +192,6 @@ async def test_async_client_context_manager_exception(self): # Should not raise additional exceptions during cleanup -class TestReflectapiOptionEdgeCases: - """Test edge cases for ReflectapiOption.""" - - def test_option_with_complex_objects(self): - """Test ReflectapiOption with complex nested objects.""" - complex_data = { - "nested": { - "list": [1, 2, {"deep": "value"}], - "tuple": (1, 2, 3), - "set": {1, 2, 3}, # Sets are not JSON serializable - } - } - - option = ReflectapiOption(complex_data) - assert option.is_some - assert option.unwrap() == complex_data - - def test_option_with_large_data(self): - """Test ReflectapiOption with very large data.""" - large_string = "x" * 1000000 # 1MB string - option = ReflectapiOption(large_string) - - assert option.is_some - assert len(option.unwrap()) == 1000000 - - def test_option_equality_with_different_types(self): - """Test ReflectapiOption equality with different types.""" - option_int = ReflectapiOption(42) - option_str = ReflectapiOption("42") - option_list = ReflectapiOption([42]) - - assert option_int != option_str - assert option_int != option_list # Different types - assert option_str != option_list # Different types - - def test_option_hash_with_unhashable_values(self): - """Test ReflectapiOption hash with unhashable values.""" - unhashable_data = {"key": [1, 2, 3]} # Lists are unhashable - option = ReflectapiOption(unhashable_data) - - # Should raise TypeError when trying to hash - with pytest.raises(TypeError): - hash(option) - - def test_option_map_chain(self): - """Test chaining multiple map operations.""" - option = ReflectapiOption(10) - result = option.map(lambda x: x * 2).map(lambda x: x + 5).map(str) - - assert result.is_some - assert result.unwrap() == "25" - - def test_option_filter_with_exception_in_predicate(self): - """Test filter with predicate that raises exception.""" - option = ReflectapiOption("test") - - def bad_predicate(x): - raise ValueError("Predicate error") - - # Should propagate the exception - with pytest.raises(ValueError, match="Predicate error"): - option.filter(bad_predicate) - - def test_option_map_with_exception_in_function(self): - """Test map with function that raises exception.""" - option = ReflectapiOption(10) - - def bad_function(x): - raise RuntimeError("Function error") - - # Should propagate the exception - with pytest.raises(RuntimeError, match="Function error"): - option.map(bad_function) - - class TestErrorHandlingEdgeCases: """Test edge cases in error handling.""" @@ -356,68 +283,6 @@ def test_application_error_with_none_response(self): class TestSerializationEdgeCases: """Test edge cases in serialization.""" - def test_serialize_option_dict_with_nested_options(self): - """Test serializing dictionary with nested ReflectapiOption values.""" - from reflectapi_runtime.option import serialize_option_dict - - nested_data = { - "level1": { - "level2": { - "option_field": ReflectapiOption("value"), - "undefined_field": ReflectapiOption(Undefined), - "none_field": ReflectapiOption(None), - } - }, - "list_with_options": [ - ReflectapiOption("item1"), - ReflectapiOption(Undefined), - ReflectapiOption(None), - "regular_item", - ], - } - - result = serialize_option_dict(nested_data) - - # Should properly handle nested structures - assert result["level1"]["level2"]["option_field"] == "value" - assert "undefined_field" not in result["level1"]["level2"] - assert result["level1"]["level2"]["none_field"] is None - - # Should handle lists with options - expected_list = ["item1", None, "regular_item"] - assert result["list_with_options"] == expected_list - - def test_serialize_option_dict_with_circular_references(self): - """Test serializing dictionary with circular references.""" - from reflectapi_runtime.option import serialize_option_dict - - # Create circular reference - data = {"option": ReflectapiOption("value"), "nested": {}} - data["nested"]["parent"] = data - - # Should handle circular references gracefully or raise appropriate error - try: - result = serialize_option_dict(data) - # If it succeeds, verify the option was processed - assert result["option"] == "value" - except RecursionError: - # Circular references might cause recursion error - that's acceptable - pass - - def test_pydantic_model_with_invalid_reflectapi_option(self): - """Test Pydantic model creation and validation.""" - - class TestModel(BaseModel): - field: ReflectapiOption[str] = ReflectapiOption() - - # Should work with valid data - model = TestModel(field=ReflectapiOption("valid")) - assert model.field.unwrap() == "valid" - - # Should work with automatic wrapping - model2 = TestModel(field="auto_wrapped") - assert model2.field.unwrap() == "auto_wrapped" - class TestAuthEdgeCases: """Test edge cases in authentication.""" @@ -527,21 +392,6 @@ def test_client_with_many_headers(self): client = ClientBase("https://api.example.com", headers=large_headers) assert hasattr(client, "_client") - def test_reflectapi_option_memory_usage(self): - """Test ReflectapiOption memory usage with large data.""" - import sys - - # Create option with large data - large_data = list(range(100000)) - option = ReflectapiOption(large_data) - - # Should not use significantly more memory than the data itself - option_size = sys.getsizeof(option) - data_size = sys.getsizeof(large_data) - - # Option should not add significant overhead - assert option_size < data_size * 1.1 # Less than 10% overhead - def test_transport_metadata_with_large_headers(self): """Test TransportMetadata with very large headers.""" large_headers = {f"header_{i}": "x" * 1000 for i in range(100)} diff --git a/reflectapi-python-runtime/tests/test_enhanced_features.py b/reflectapi-python-runtime/tests/test_enhanced_features.py index 2e48cf34..d49f16d2 100644 --- a/reflectapi-python-runtime/tests/test_enhanced_features.py +++ b/reflectapi-python-runtime/tests/test_enhanced_features.py @@ -497,67 +497,53 @@ def __init__(self, base_url, **kwargs): class TestIntegration: """Test integration between different enhanced features.""" - def test_option_serialization_with_apiresponse(self): - """Test Option serialization working with ApiResponse.""" - from reflectapi_runtime import ( - ReflectapiOption, - Undefined, - serialize_option_dict, - ) + def test_partial_model_serialization_with_apiresponse(self): + """A ReflectapiPartialModel inside ApiResponse round-trips correctly.""" + from reflectapi_runtime import ReflectapiPartialModel + + class User(ReflectapiPartialModel): + name: str + age: int | None = None + email: str | None = None + active: bool | None = None - # Create response data with Options - response_data = { - "name": "test", - "age": ReflectapiOption(25), - "email": ReflectapiOption(Undefined), - "active": ReflectapiOption(None), - } + # Build a model with mixed states: a value, an explicit null, and an absent field. + model = User(name="test", age=25, active=None) metadata = TransportMetadata( status_code=200, headers=httpx.Headers({}), timing=0.1, raw_response=Mock() ) + response = ApiResponse(model, metadata) - response = ApiResponse(response_data, metadata) - - # Test that we can access the data through delegation - assert response["name"] == "test" - assert response["age"].unwrap() == 25 - - # Test serialization - serialized = serialize_option_dict(response.value) - expected = { - "name": "test", - "age": 25, - "active": None, - # email excluded because it's undefined - } - assert serialized == expected + # Wire shape: only kwargs we passed land in the payload. + # `email` was not set → omitted entirely (not even `null`). + assert response.value.model_dump_json() == ( + '{"name":"test","age":25,"active":null}' + ) def test_complete_workflow(self): - """Test complete workflow with all enhanced features.""" - # This would be a comprehensive integration test in a real scenario - # For now, just verify that all components can be imported and instantiated - + """Smoke-test that the public exports cooperate end-to-end.""" from reflectapi_runtime import ( ApiResponse, CassetteClient, - ReflectapiOption, + ReflectapiPartialModel, TestClientMixin, TransportMetadata, - Undefined, ) - # Create Option - option = ReflectapiOption(42) - assert option.is_some + class Item(ReflectapiPartialModel): + kind: str | None = None - # Create CassetteClient + # The partial-model serialiser omits the field because it wasn't set. + assert Item().model_dump_json() == "{}" + + # CassetteClient still works. with tempfile.TemporaryDirectory() as temp_dir: cassette_path = Path(temp_dir) / "test.json" client = CassetteClient.record(cassette_path) assert client.is_recording - # Create ApiResponse with enhanced __dir__ + # ApiResponse __dir__ still surfaces fields from delegated objects. metadata = TransportMetadata( status_code=200, headers=httpx.Headers({}), timing=0.1, raw_response=Mock() ) @@ -566,6 +552,3 @@ def test_complete_workflow(self): assert "data" in dir_result assert "value" in dir_result assert "metadata" in dir_result - - # All components working together - assert True # If we get here, everything imported and worked correctly diff --git a/reflectapi-python-runtime/tests/test_option.py b/reflectapi-python-runtime/tests/test_option.py deleted file mode 100644 index 57ce7cf1..00000000 --- a/reflectapi-python-runtime/tests/test_option.py +++ /dev/null @@ -1,411 +0,0 @@ -"""Tests for Option handling.""" - -import os -import sys - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) - -import pytest -from pydantic import BaseModel - -from reflectapi_runtime import ( - Option, - ReflectapiOption, - Undefined, - none, - serialize_option_dict, - some, - undefined, -) - - -class TestUndefinedType: - """Test the _UndefinedType sentinel.""" - - def test_singleton_behavior(self): - """Test that Undefined behaves as a singleton.""" - from reflectapi_runtime.option import _UndefinedType - - another_undefined = _UndefinedType() - assert Undefined == another_undefined - assert hash(Undefined) == hash(another_undefined) - - def test_string_representation(self): - """Test string representations.""" - assert str(Undefined) == "Undefined" - assert repr(Undefined) == "Undefined" - - def test_boolean_conversion(self): - """Test boolean conversion.""" - assert not Undefined - assert bool(Undefined) is False - - -class TestReflectapiOption: - """Test ReflectapiOption functionality.""" - - def test_default_constructor(self): - """Test default constructor creates undefined option.""" - option = ReflectapiOption() - assert option.is_undefined - assert not option.is_none - assert not option.is_some - - def test_undefined_constructor(self): - """Test constructor with Undefined.""" - option = ReflectapiOption(Undefined) - assert option.is_undefined - assert not option.is_none - assert not option.is_some - - def test_none_constructor(self): - """Test constructor with None.""" - option = ReflectapiOption(None) - assert not option.is_undefined - assert option.is_none - assert not option.is_some - - def test_value_constructor(self): - """Test constructor with actual value.""" - option = ReflectapiOption(42) - assert not option.is_undefined - assert not option.is_none - assert option.is_some - - def test_value_property_undefined(self): - """Test value property with undefined option.""" - option = ReflectapiOption(Undefined) - - with pytest.raises(ValueError, match="Cannot access value of undefined option"): - _ = option.value - - def test_value_property_none(self): - """Test value property with None.""" - option = ReflectapiOption(None) - assert option.value is None - - def test_value_property_some(self): - """Test value property with actual value.""" - option = ReflectapiOption(42) - assert option.value == 42 - - def test_unwrap_undefined(self): - """Test unwrap with undefined option.""" - option = ReflectapiOption(Undefined) - - with pytest.raises(ValueError, match="Cannot unwrap undefined option"): - option.unwrap() - - def test_unwrap_none(self): - """Test unwrap with None.""" - option = ReflectapiOption(None) - - with pytest.raises(ValueError, match="Cannot unwrap None option"): - option.unwrap() - - def test_unwrap_some(self): - """Test unwrap with actual value.""" - option = ReflectapiOption(42) - assert option.unwrap() == 42 - - def test_unwrap_or_undefined(self): - """Test unwrap_or with undefined option.""" - option = ReflectapiOption(Undefined) - assert option.unwrap_or(99) == 99 - - def test_unwrap_or_none(self): - """Test unwrap_or with None.""" - option = ReflectapiOption(None) - assert option.unwrap_or(99) == 99 - - def test_unwrap_or_some(self): - """Test unwrap_or with actual value.""" - option = ReflectapiOption(42) - assert option.unwrap_or(99) == 42 - - def test_map_undefined(self): - """Test map with undefined option.""" - option = ReflectapiOption(Undefined) - result = option.map(lambda x: x * 2) - - assert result.is_undefined - - def test_map_none(self): - """Test map with None.""" - option = ReflectapiOption(None) - result = option.map(lambda x: x * 2) - - assert result.is_none - - def test_map_some(self): - """Test map with actual value.""" - option = ReflectapiOption(42) - result = option.map(lambda x: x * 2) - - assert result.is_some - assert result.unwrap() == 84 - - def test_filter_undefined(self): - """Test filter with undefined option.""" - option = ReflectapiOption(Undefined) - result = option.filter(lambda x: x > 10) - - assert result.is_undefined - - def test_filter_none(self): - """Test filter with None.""" - option = ReflectapiOption(None) - result = option.filter(lambda x: x > 10) - - assert result.is_undefined - - def test_filter_some_true(self): - """Test filter with value that passes predicate.""" - option = ReflectapiOption(42) - result = option.filter(lambda x: x > 10) - - assert result.is_some - assert result.unwrap() == 42 - - def test_filter_some_false(self): - """Test filter with value that fails predicate.""" - option = ReflectapiOption(5) - result = option.filter(lambda x: x > 10) - - assert result.is_undefined - - def test_equality_options(self): - """Test equality between options.""" - option1 = ReflectapiOption(42) - option2 = ReflectapiOption(42) - option3 = ReflectapiOption(99) - option4 = ReflectapiOption(None) - option5 = ReflectapiOption(Undefined) - - assert option1 == option2 - assert option1 != option3 - assert option1 != option4 - assert option1 != option5 - assert option5 == ReflectapiOption(Undefined) - - def test_equality_values(self): - """Test equality with raw values.""" - option = ReflectapiOption(42) - - assert option == 42 - assert option != 99 - assert option is not None - assert option != Undefined - - def test_string_representations(self): - """Test string representations.""" - undefined_option = ReflectapiOption(Undefined) - none_option = ReflectapiOption(None) - some_option = ReflectapiOption(42) - - assert str(undefined_option) == "Undefined" - assert str(none_option) == "None" - assert str(some_option) == "42" - - assert repr(undefined_option) == "ReflectapiOption(Undefined)" - assert repr(none_option) == "ReflectapiOption(None)" - assert repr(some_option) == "ReflectapiOption(42)" - - def test_boolean_conversion(self): - """Test boolean conversion.""" - assert not ReflectapiOption(Undefined) - assert not ReflectapiOption(None) - assert not ReflectapiOption(0) - assert not ReflectapiOption("") - assert not ReflectapiOption([]) - assert ReflectapiOption(42) - assert ReflectapiOption("hello") - assert ReflectapiOption([1, 2, 3]) - - def test_hash(self): - """Test hash functionality.""" - option1 = ReflectapiOption(42) - option2 = ReflectapiOption(42) - option3 = ReflectapiOption(99) - - assert hash(option1) == hash(option2) - assert hash(option1) != hash(option3) - - # Test that options can be used in sets - option_set = {option1, option2, option3} - assert len(option_set) == 2 # option1 and option2 are equal - - -class TestConvenienceFunctions: - """Test convenience functions.""" - - def test_some_function(self): - """Test some() function.""" - option = some(42) - assert option.is_some - assert option.unwrap() == 42 - - def test_none_function(self): - """Test none() function.""" - option = none() - assert option.is_none - assert option.value is None - - def test_undefined_function(self): - """Test undefined() function.""" - option = undefined() - assert option.is_undefined - - -class TestSerializeOptionDict: - """Test serialize_option_dict function.""" - - def test_simple_dict_no_options(self): - """Test serialization of dict without options.""" - data = {"name": "test", "value": 42} - result = serialize_option_dict(data) - - assert result == data - - def test_simple_dict_with_undefined(self): - """Test serialization excluding undefined options.""" - data = {"name": "test", "age": ReflectapiOption(Undefined), "value": 42} - result = serialize_option_dict(data) - - expected = {"name": "test", "value": 42} - assert result == expected - - def test_simple_dict_with_none(self): - """Test serialization including None options.""" - data = {"name": "test", "age": ReflectapiOption(None), "value": 42} - result = serialize_option_dict(data) - - expected = {"name": "test", "age": None, "value": 42} - assert result == expected - - def test_simple_dict_with_some(self): - """Test serialization including Some options.""" - data = {"name": "test", "age": ReflectapiOption(25), "value": 42} - result = serialize_option_dict(data) - - expected = {"name": "test", "age": 25, "value": 42} - assert result == expected - - def test_nested_dict(self): - """Test serialization of nested dictionaries.""" - data = { - "user": { - "name": "test", - "age": ReflectapiOption(25), - "email": ReflectapiOption(Undefined), - }, - "metadata": {"version": ReflectapiOption(None), "enabled": True}, - } - result = serialize_option_dict(data) - - expected = { - "user": {"name": "test", "age": 25}, - "metadata": {"version": None, "enabled": True}, - } - assert result == expected - - def test_list_with_options(self): - """Test serialization of lists containing options.""" - data = { - "items": [ - ReflectapiOption(1), - ReflectapiOption(None), - ReflectapiOption(Undefined), - ReflectapiOption(3), - 42, # Non-option value - ] - } - result = serialize_option_dict(data) - - expected = { - "items": [1, None, 3, 42] # Undefined option filtered out - } - assert result == expected - - def test_complex_nested_structure(self): - """Test serialization of complex nested structure.""" - data = { - "users": [ - { - "name": "Alice", - "age": ReflectapiOption(30), - "email": ReflectapiOption(Undefined), - }, - { - "name": "Bob", - "age": ReflectapiOption(None), - "email": "bob@example.com", - }, - ], - "config": { - "debug": ReflectapiOption(True), - "timeout": ReflectapiOption(Undefined), - "retries": 3, - }, - } - result = serialize_option_dict(data) - - expected = { - "users": [ - {"name": "Alice", "age": 30}, - {"name": "Bob", "age": None, "email": "bob@example.com"}, - ], - "config": {"debug": True, "retries": 3}, - } - assert result == expected - - -class TestPydanticIntegration: - """Test integration with Pydantic models.""" - - def test_pydantic_model_with_options(self): - """Test Option handling with Pydantic models.""" - - class UserModel(BaseModel): - name: str - age: ReflectapiOption[int] = ReflectapiOption(Undefined) - email: ReflectapiOption[str] = ReflectapiOption(Undefined) - - # Test model creation - user = UserModel(name="Alice", age=some(30), email=none()) - - assert user.name == "Alice" - assert user.age.is_some - assert user.age.unwrap() == 30 - assert user.email.is_none - - # Test serialization - model_dict = user.model_dump() if hasattr(user, "model_dump") else user.dict() - processed_dict = serialize_option_dict(model_dict) - - expected = {"name": "Alice", "age": 30, "email": None} - assert processed_dict == expected - - def test_pydantic_model_with_undefined_fields(self): - """Test Pydantic model with undefined fields.""" - - class UpdateRequest(BaseModel): - name: str - age: ReflectapiOption[int] = ReflectapiOption(Undefined) - email: ReflectapiOption[str] = ReflectapiOption(Undefined) - - # Only provide name, leave others undefined - request = UpdateRequest(name="Alice") - - assert request.name == "Alice" - assert request.age.is_undefined - assert request.email.is_undefined - - # Serialization should exclude undefined fields - model_dict = ( - request.model_dump() if hasattr(request, "model_dump") else request.dict() - ) - processed_dict = serialize_option_dict(model_dict) - - expected = {"name": "Alice"} - assert processed_dict == expected diff --git a/reflectapi-python-runtime/tests/test_partial.py b/reflectapi-python-runtime/tests/test_partial.py new file mode 100644 index 00000000..8873c577 --- /dev/null +++ b/reflectapi-python-runtime/tests/test_partial.py @@ -0,0 +1,233 @@ +"""Tests for ``ReflectapiPartialModel``. + +These tests pin the wire-format contract that replaced the now-deleted +``ReflectapiOption`` class: a field that was *never explicitly set* on +the instance is omitted from the serialised payload entirely; a field +set to ``None`` is emitted as ``null``; a field set to a value is +emitted as that value. Round-trip preserves the distinction via +``model_fields_set``. +""" + +from __future__ import annotations + +import json + +import pytest +from pydantic import BaseModel, ValidationError + +from reflectapi_runtime import ReflectapiPartialModel + + +class Snapshot(BaseModel): + """Plain Pydantic model used as an inner type to exercise nested validation.""" + + name: str + description: str | None = None + + +class Item(ReflectapiPartialModel): + identity: str + snapshot: Snapshot | None = None + age: int | None = None + + +class PartialSnapshot(ReflectapiPartialModel): + """Partial inner type — its own absent/null distinction must survive.""" + + name: str + description: str | None = None + + +class PartialItem(ReflectapiPartialModel): + identity: str + snapshot: PartialSnapshot | None = None + + +class TestModelFieldsSetTracking: + """``model_fields_set`` must reflect what was on the wire.""" + + def test_absent_key_not_in_fields_set(self): + item = Item.model_validate({"identity": "x"}) + assert item.snapshot is None + assert "snapshot" not in item.model_fields_set + + def test_explicit_null_in_fields_set(self): + item = Item.model_validate({"identity": "x", "snapshot": None}) + assert item.snapshot is None + assert "snapshot" in item.model_fields_set + + def test_value_in_fields_set(self): + item = Item.model_validate( + {"identity": "x", "snapshot": {"name": "n"}} + ) + assert isinstance(item.snapshot, Snapshot) + assert "snapshot" in item.model_fields_set + + def test_kwargs_construction_only_marks_passed_keys(self): + item = Item(identity="x", age=4) + assert item.model_fields_set == {"identity", "age"} + assert "snapshot" not in item.model_fields_set + + +class TestWireFormat: + """Serialisation must omit unset fields, keep explicit nulls, and emit values.""" + + def test_unset_field_omitted_on_wire(self): + item = Item(identity="x") + assert item.model_dump_json() == '{"identity":"x"}' + + def test_explicit_null_appears_on_wire(self): + item = Item(identity="x", snapshot=None) + payload = json.loads(item.model_dump_json()) + assert payload == {"identity": "x", "snapshot": None} + + def test_value_appears_on_wire(self): + item = Item.model_validate( + {"identity": "x", "snapshot": {"name": "n"}, "age": 4} + ) + payload = json.loads(item.model_dump_json()) + assert payload == { + "identity": "x", + "snapshot": {"name": "n", "description": None}, + "age": 4, + } + + def test_post_construction_assignment_lands_on_wire(self): + """Caveat: requires ``validate_assignment=True`` in ``model_config``. + + Generated client classes always emit that flag; constructing a + ``ReflectapiPartialModel`` subclass in user code without it (this + base ``Item`` model_config doesn't include it) leaves + ``model_fields_set`` untouched on assignment. This test pins the + behaviour for the *generated* shape — we patch the config locally + to match what the codegen emits. + """ + + class WithAssign(ReflectapiPartialModel): + model_config = {"validate_assignment": True} + name: str + note: str | None = None + + m = WithAssign(name="x") + assert m.model_dump_json() == '{"name":"x"}' + m.note = None + assert m.model_dump_json() == '{"name":"x","note":null}' + + +class TestNestedPartial: + """A partial model inside a partial model preserves its own absent/null distinction.""" + + def test_nested_absent_field_omitted(self): + item = PartialItem.model_validate( + {"identity": "x", "snapshot": {"name": "n"}} + ) + # description was absent in the nested wire dict — must stay absent. + assert item.model_dump_json() == ( + '{"identity":"x","snapshot":{"name":"n"}}' + ) + + def test_nested_explicit_null_emitted(self): + item = PartialItem.model_validate( + {"identity": "x", "snapshot": {"name": "n", "description": None}} + ) + assert item.model_dump_json() == ( + '{"identity":"x","snapshot":{"name":"n","description":null}}' + ) + + +class TestInnerTypeValidation: + """Pydantic validates inner types normally — no custom wrapper to bypass.""" + + def test_inner_dict_becomes_typed_instance(self): + item = Item.model_validate( + {"identity": "x", "snapshot": {"name": "Bumper"}} + ) + assert isinstance(item.snapshot, Snapshot) + assert item.snapshot.name == "Bumper" + + def test_garbage_inner_data_rejected(self): + with pytest.raises(ValidationError) as exc_info: + Item.model_validate( + {"identity": "x", "snapshot": {"wrong_field": True}} + ) + # The error path includes 'snapshot.name' to show the inner + # validator ran. Pre-fix it would have silently accepted the dict. + loc = exc_info.value.errors()[0]["loc"] + assert "snapshot" in loc and "name" in loc + + def test_container_of_models_is_validated(self): + class Bag(ReflectapiPartialModel): + items: list[Snapshot] | None = None + + bag = Bag.model_validate({"items": [{"name": "a"}, {"name": "b"}]}) + assert all(isinstance(s, Snapshot) for s in bag.items) + assert [s.name for s in bag.items] == ["a", "b"] + + +class TestRoundTrip: + """JSON round-trip must preserve which fields were on the wire.""" + + def test_absent_field_stays_absent(self): + item = Item.model_validate({"identity": "x"}) + reloaded = Item.model_validate_json(item.model_dump_json()) + assert "snapshot" not in reloaded.model_fields_set + assert reloaded.model_dump_json() == '{"identity":"x"}' + + def test_explicit_null_stays_null(self): + item = Item.model_validate({"identity": "x", "snapshot": None}) + reloaded = Item.model_validate_json(item.model_dump_json()) + assert "snapshot" in reloaded.model_fields_set + assert reloaded.snapshot is None + assert json.loads(reloaded.model_dump_json()) == { + "identity": "x", + "snapshot": None, + } + + def test_value_round_trips_as_typed_instance(self): + item = Item.model_validate( + {"identity": "x", "snapshot": {"name": "n"}} + ) + reloaded = Item.model_validate_json(item.model_dump_json()) + assert isinstance(reloaded.snapshot, Snapshot) + + +class TestEdgeCases: + def test_field_with_default_value_omitted_when_not_provided(self): + """A field whose declared default *isn't* ``None`` is still omitted when unset.""" + + class M(ReflectapiPartialModel): + count: int = 0 + + # Default applies (count == 0) but wasn't on the wire. + m = M() + assert m.count == 0 + assert m.model_dump_json() == "{}" + + def test_field_provided_at_default_value_appears_on_wire(self): + class M(ReflectapiPartialModel): + count: int = 0 + + m = M(count=0) + assert m.model_dump_json() == '{"count":0}' + + def test_alias_respected_on_serialise(self): + from pydantic import Field + + class M(ReflectapiPartialModel): + class_: str | None = Field(default=None, alias="class") + + m = M.model_validate({"class": "x"}) + assert m.model_dump_json(by_alias=True) == '{"class":"x"}' + + def test_unset_alias_omitted_on_serialise(self): + from pydantic import Field + + class M(ReflectapiPartialModel): + class_: str | None = Field(default=None, alias="class") + + assert M().model_dump_json(by_alias=True) == "{}" + + def test_model_dump_python_obeys_same_rule(self): + """`model_dump()` (Python dict) must also drop unset keys.""" + item = Item(identity="x") + assert item.model_dump() == {"identity": "x"} diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index 634678cf..9ebe70c7 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -162,16 +162,12 @@ fn python_type_mappings() -> &'static HashMap<&'static str, PythonTypeMapping> { m.insert("std::result::Result", PythonTypeMapping::simple("T | E")); // ReflectAPI runtime types - m.insert( - "reflectapi::Option", - PythonTypeMapping { - type_hint: "ReflectapiOption[T]", - imports: &[], - runtime_imports: &["ReflectapiOption"], - provided_by_runtime: true, - ignore_type_arguments: false, - }, - ); + // The three-state Option from reflectapi-schema renders as plain + // `T | None`. The "absent on the wire" state is preserved at the + // model level: classes with any `reflectapi::Option<_>` field + // inherit from `ReflectapiPartialModel`, whose `@model_serializer` + // drops fields not in `model_fields_set`. + m.insert("reflectapi::Option", PythonTypeMapping::simple("T | None")); m.insert( "reflectapi::Empty", PythonTypeMapping { @@ -395,6 +391,10 @@ fn generate_optimized_imports(imports: &templates::Imports) -> String { runtime_imports.insert("ClientBase, ApiResponse".to_string()); } + if imports.has_partial_models { + runtime_imports.insert("ReflectapiPartialModel".to_string()); + } + runtime_imports.extend(imports.extra_runtime_imports.iter().cloned()); // Build the final import string @@ -715,6 +715,8 @@ fn render_struct_with_flattened_internal_enum( Some("None".to_string()) }, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } @@ -767,6 +769,8 @@ fn render_struct_with_flattened_internal_enum( Some("None".to_string()) }, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } } @@ -790,6 +794,8 @@ fn render_struct_with_flattened_internal_enum( optional: false, default_value: Some(format!("\"{}\"", variant.serde_name())), alias: tag_alias.clone(), + + is_partial: false, }); // Add variant-specific fields @@ -815,6 +821,8 @@ fn render_struct_with_flattened_internal_enum( optional, default_value, alias, + + is_partial: vf.type_ref.name == "reflectapi::Option", }); } } @@ -877,6 +885,8 @@ fn render_struct_with_flattened_internal_enum( Some("None".to_string()) }, alias, + + is_partial: sf.type_ref.name == "reflectapi::Option", }); } } @@ -963,6 +973,8 @@ fn render_struct_with_flatten_standard( Some("None".to_string()) }, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } @@ -995,6 +1007,27 @@ fn render_struct_with_flatten_standard( } /// Validate that all type references in the schema exist +/// Walk every input/output struct in the schema and return `true` if +/// any field uses the three-state `reflectapi::Option`. Used to +/// decide whether to import `ReflectapiPartialModel` into the generated +/// bundle. Cheap — runs once per generation. +fn schema_has_partial_field(schema: &Schema) -> bool { + fn iter_typespace_has_partial(typespace: &reflectapi_schema::Typespace) -> bool { + typespace.types().any(|t| match t { + reflectapi_schema::Type::Struct(s) => { + s.fields().any(|f| f.type_ref.name == "reflectapi::Option") + } + reflectapi_schema::Type::Enum(e) => e + .variants + .iter() + .any(|v| v.fields().any(|f| f.type_ref.name == "reflectapi::Option")), + _ => false, + }) + } + iter_typespace_has_partial(&schema.input_types) + || iter_typespace_has_partial(&schema.output_types) +} + fn validate_type_references(schema: &Schema) -> anyhow::Result<()> { // Collect all defined types let mut defined_types = HashSet::new(); @@ -1525,6 +1558,7 @@ fn build_python_generation( let has_streaming = schema .functions() .any(|f| matches!(f.output_type, OutputType::Stream { .. })); + let has_partial_models = schema_has_partial_field(&schema); let python_metadata = collect_python_metadata_usage(&all_type_names); // Generate imports @@ -1543,6 +1577,7 @@ fn build_python_generation( has_externally_tagged_enums, global_type_vars: Vec::new(), // Will be added later after tracking usage has_streaming, + has_partial_models, }; // Use optimized import generation instead of template generated_code.push(generate_optimized_imports(&imports)); @@ -2640,6 +2675,8 @@ fn collect_flattened_fields( optional: true, default_value: Some("None".to_string()), alias: None, + + is_partial: false, }]); } @@ -2775,6 +2812,8 @@ fn make_flattened_field( optional, default_value, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }) } @@ -2835,6 +2874,8 @@ fn collect_flattened_enum_fields( optional, default_value, alias, + + is_partial: false, }); Ok(()) @@ -2903,6 +2944,8 @@ fn render_struct( optional, default_value, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }) }) .collect::, anyhow::Error>>()?; @@ -3070,6 +3113,8 @@ fn render_adjacently_tagged_enum( optional: false, default_value: discriminator_default_value, alias: None, + + is_partial: false, }]; // Add the content field based on variant type @@ -3096,6 +3141,8 @@ fn render_adjacently_tagged_enum( optional: false, default_value: None, alias: None, + + is_partial: false, }); } else { // Multi-field tuple: content is a list @@ -3117,6 +3164,8 @@ fn render_adjacently_tagged_enum( optional: false, default_value: None, alias: None, + + is_partial: false, }); } } @@ -3145,6 +3194,8 @@ fn render_adjacently_tagged_enum( optional, default_value, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } let content_model = templates::DataClass { @@ -3171,6 +3222,8 @@ fn render_adjacently_tagged_enum( optional: false, default_value: None, alias: None, + + is_partial: false, }); } } @@ -3302,6 +3355,8 @@ fn render_externally_tagged_enum( optional: false, default_value: None, alias: None, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } @@ -3379,6 +3434,8 @@ fn render_externally_tagged_enum( optional, default_value, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } @@ -3522,6 +3579,8 @@ fn render_internally_tagged_enum( optional: false, default_value: discriminator_default_value, alias: None, + + is_partial: false, }]; // Handle variant fields based on type @@ -3615,6 +3674,8 @@ fn render_internally_tagged_enum( optional, default_value, alias, + + is_partial: struct_field.type_ref.name == "reflectapi::Option", }); } } @@ -3637,6 +3698,8 @@ fn render_internally_tagged_enum( optional: false, default_value: None, alias: None, + + is_partial: inner_field.type_ref.name == "reflectapi::Option", }); } } @@ -3665,6 +3728,8 @@ fn render_internally_tagged_enum( optional, default_value, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } } @@ -3807,6 +3872,8 @@ fn render_untagged_enum( optional, default_value, alias, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } } @@ -3835,6 +3902,8 @@ fn render_untagged_enum( optional, default_value, alias: None, + + is_partial: field.type_ref.name == "reflectapi::Option", }); } } @@ -4367,7 +4436,6 @@ fn improve_class_name_part(name_part: &str) -> String { ("StdTuple", "Tuple"), ("StdCollectionsHashMap", "HashMap"), ("StdCollectionsBTreeMap", "BTreeMap"), - ("ReflectapiOption", "ReflectapiOption"), // Keep this one // Handle namespace prefixes ("Crate::", ""), ("Self::", ""), @@ -5042,6 +5110,10 @@ pub mod templates { pub global_type_vars: Vec, /// At least one streaming endpoint is exposed by the client. pub has_streaming: bool, + /// At least one generated class has a `reflectapi::Option` + /// field, so the bundle needs to import + /// `ReflectapiPartialModel`. + pub has_partial_models: bool, } pub struct DataClass { @@ -5054,13 +5126,25 @@ pub mod templates { } impl DataClass { + /// True iff any field came from `reflectapi::Option`; the + /// containing class then needs `ReflectapiPartialModel` so that + /// `model_fields_set` drives wire-format presence. + fn is_partial(&self) -> bool { + self.fields.iter().any(|f| f.is_partial) + } + pub fn render(&self) -> String { let mut s = String::new(); + let base_class = if self.is_partial() { + "ReflectapiPartialModel" + } else { + "BaseModel" + }; if self.is_generic { let params = self.generic_params.join(", "); - writeln!(s, "class {}(BaseModel, Generic[{}]):", self.name, params).unwrap(); + writeln!(s, "class {}({base_class}, Generic[{}]):", self.name, params).unwrap(); } else { - writeln!(s, "class {}(BaseModel):", self.name).unwrap(); + writeln!(s, "class {}({base_class}):", self.name).unwrap(); } if let Some(desc) = &self.description { let desc = super::sanitize_for_docstring(desc); @@ -5069,11 +5153,17 @@ pub mod templates { } } writeln!(s).unwrap(); - writeln!( - s, - " model_config = ConfigDict(extra=\"ignore\", populate_by_name=True)" - ) - .unwrap(); + // `validate_assignment=True` for partial models keeps + // `model_fields_set` in sync when callers do + // `m.field = value` after construction (so the assignment + // actually lands on the wire). Plain BaseModel classes + // don't need it. + let config = if self.is_partial() { + "ConfigDict(extra=\"ignore\", populate_by_name=True, validate_assignment=True)" + } else { + "ConfigDict(extra=\"ignore\", populate_by_name=True)" + }; + writeln!(s, " model_config = {config}").unwrap(); writeln!(s).unwrap(); for field in &self.fields { write!(s, " {}: {}", field.name, field.type_annotation).unwrap(); @@ -5682,6 +5772,11 @@ pub mod templates { pub optional: bool, pub default_value: Option, pub alias: Option, + /// `true` iff this field came from `reflectapi::Option` in the + /// source schema (the three-state Option). The renderer uses this + /// to decide whether the containing class needs to inherit from + /// `ReflectapiPartialModel`. + pub is_partial: bool, } pub struct EnumVariant { From 13987db2b824936255acd1461125e20c6f82fb90 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 11:40:10 +1200 Subject: [PATCH 02/17] codegen(python): fix four dangling-reference bugs + strict rebuild MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four user-reported codegen bugs all share one root cause: the codegen emitted Python type annotations referencing symbols it didn't actually define. They went undetected because the generated `_rebuild_models()` helper wrapped `model_rebuild()` in `try: ... except Exception: pass`, so dangling references only surfaced when users instantiated affected models. Fixes are below; the central protection is the strict-rebuild contract — the entire class of "codegen emitted a dangling reference" bugs now fails fast at import time and a new CI job pins it. ## The four bugs 1. Namespace alias mismatch (570 model_rebuild failures in core-server) `myapi/order/__init__.py` aliased classes with the namespace cap stripped (`InsertData = MyapiOrderInsertData`) while field annotations elsewhere referenced the un-stripped Rust leaf (`order.OrderInsertData`). The latter never resolved. Root cause: `module_aliases()` and `type_name_to_python_ref()` stripped names with two different rules. Fix: namespaces now expose *both* the stripped form (ergonomic public name) and the Rust leaf (matches field annotations). `ModuleType` carries the original Rust path so the alias generator can compute the leaf. 2. `std.tuple.TupleN[A, B]` with no `std.tuple` class (6 occurrences) Rust tuples `(A, B)` were rendered as `std.tuple.Tuple2[A, B]` but no `std.tuple` submodule was ever generated. Root cause: only `Tuple0` had a mapping; arities 1..16 fell through to the default dotted-path renderer. Fix: map every `std::tuple::TupleN` to Python's built-in `tuple` — Pydantic v2 validates `tuple[A, B, …]` as a fixed-arity heterogeneous tuple, exactly matching serde's JSON-array wire shape. 3. `std::time::Duration` rendered as `timedelta` (9 occurrences) serde emits `Duration` as `{"secs": , "nanos": }`. Pydantic's bare `timedelta` validator rejects the dict shape. Root cause: codegen mapped `std::time::Duration` to `timedelta` based on the Rust type name without considering serde's actual JSON shape. Fix: new `ReflectapiDuration` runtime type — an `Annotated` wrapper around `timedelta` with `BeforeValidator` (accepts `{secs, nanos}` and existing `timedelta` instances) and `PlainSerializer` (emits `{secs, nanos}` so the wire round-trips). Microsecond precision; documented caveat for sub-µs `nanos`. 4. `std.marker.PhantomData[T]` with no `std.marker` class (NEW) serde renders `PhantomData` as JSON `null`, and reflectapi-derive surfaces the field in the schema. The codegen rendered an annotation referencing a non-existent `std.marker.PhantomData` class. Root cause: PhantomData is a type-system-only marker — it carries no wire data and shouldn't appear in the Python model at all. Fix: new `strip_phantom_data_fields()` schema preprocessor drops every `std::marker::PhantomData<_>` field before rendering. Adds `Fields::retain()` to `reflectapi-schema` for the surgery. ## Validation harness The deeper problem was the silent error swallowing in `_rebuild.py`: try: _model.model_rebuild() except Exception: pass This pattern made the bugs above survive every PR. The generated `_rebuild_models()` now: - Collects every `model_rebuild()` failure into one structured report. - Raises `RuntimeError` with all failures listed at once. - Skips non-Pydantic rendered types (enums) so they don't false-positive. The namespace-level `_rebuild_models()` call's bare `except Exception:` was narrowed to `except ImportError:` so partial-load races are still tolerated but rebuild failures propagate. End-to-end CI guard: - New `python-codegen-smoke` job in `.github/workflows/ci.yaml` regenerates the demo Python client and runs `python -c "import api_client"`. The strict `_rebuild_models()` runs at import time, so any future dangling reference fails CI immediately. The job also checks the demo snapshots are committed (no codegen drift). - New runtime unit tests in `tests/test_codegen_regressions.py` pin the `ReflectapiDuration` contract (8 tests). - The demo Rust crate now includes a `codegen_regression` endpoint whose types exercise all four bug patterns; the schema and demo client snapshots are regenerated. ## Test plan - `cargo fmt`, `cargo clippy -p reflectapi --all-features --all-targets -D warnings`, `cargo test --workspace` — all green. - `pytest` in `reflectapi-python-runtime` — 355 pass (8 new regression tests, 0 fail). - `import api_client` against the regenerated demo — no exceptions, `RateLimit.model_validate({'retry_after': {'secs': 30, 'nanos': 0}})` produces `timedelta(seconds=30)`, model_dump_json round-trips back to `{"secs": 30, "nanos": 0}`. --- .github/workflows/ci.yaml | 27 + .../clients/python/api_client/_client.py | 48 + .../clients/python/api_client/_rebuild.py | 30 +- .../clients/python/api_client/generated.py | 15 +- .../python/api_client/myapi/__init__.py | 41 +- .../python/api_client/myapi/model/__init__.py | 4 + .../api_client/myapi/model/input/__init__.py | 4 + .../api_client/myapi/model/output/__init__.py | 4 + .../python/api_client/myapi/order/__init__.py | 109 ++ .../python/api_client/myapi/proto/__init__.py | 4 + .../python/api_client/reflectapi/__init__.py | 6 +- .../clients/python/api_client/std/__init__.py | 64 + .../python/api_client/std/time/__init__.py | 67 + .../clients/rust/generated/src/generated.rs | 67 + .../clients/typescript/generated.ts | 101 ++ reflectapi-demo/openapi.json | 172 ++ reflectapi-demo/reflectapi.json | 251 +++ reflectapi-demo/src/lib.rs | 89 + ...tests__basic__reflectapi_deprecated-5.snap | 14 +- ...__basic__reflectapi_enum_documented-4.snap | 14 +- ...__reflectapi_enum_with_skip_variant-5.snap | 14 +- ...basic__reflectapi_struct_documented-4.snap | 14 +- ...sts__basic__reflectapi_struct_empty-5.snap | 14 +- ...i_struct_one_basic_field_static_str-4.snap | 14 +- ...ctapi_struct_one_basic_field_string-4.snap | 14 +- ..._basic_field_string_reflectapi_both-5.snap | 14 +- ...ield_string_reflectapi_both_equally-5.snap | 14 +- ...eld_string_reflectapi_both_equally2-4.snap | 14 +- ...ing_reflectapi_both_with_attributes-5.snap | 14 +- ...flectapi_struct_one_basic_field_u32-4.snap | 14 +- ...ts__basic__reflectapi_struct_option-5.snap | 14 +- ...sts__basic__reflectapi_struct_tuple-5.snap | 11 +- ...tapi_struct_with_additional_derives-5.snap | 14 +- ...ruct_with_all_primitive_type_fields-5.snap | 49 +- ...__basic__reflectapi_struct_with_arc-5.snap | 14 +- ...ectapi_struct_with_arc_pointer_only-5.snap | 14 +- ...i_struct_with_attributes_input_only-5.snap | 14 +- ..._struct_with_attributes_output_only-5.snap | 14 +- ...with_external_generic_type_fallback-5.snap | 14 +- ...ectapi_struct_with_fixed_size_array-5.snap | 14 +- ...sic__reflectapi_struct_with_hashmap-5.snap | 14 +- ...eflectapi_struct_with_hashset_field-5.snap | 14 +- ...i_struct_with_hashset_field_generic-5.snap | 14 +- ...asic__reflectapi_struct_with_nested-5.snap | 14 +- ...lectapi_struct_with_nested_external-5.snap | 14 +- ...reflectapi_struct_with_self_via_arc-5.snap | 14 +- ...__reflectapi_struct_with_skip_field-5.snap | 14 +- ...ectapi_struct_with_skip_field_input-5.snap | 14 +- ...ctapi_struct_with_skip_field_output-5.snap | 14 +- ...lectapi_struct_with_transform_array-5.snap | 14 +- ...flectapi_struct_with_transform_both-5.snap | 14 +- ...tapi_struct_with_transform_fallback-5.snap | 14 +- ...ruct_with_transform_fallback_nested-5.snap | 14 +- ...lectapi_struct_with_transform_input-5.snap | 14 +- ...ectapi_struct_with_transform_output-5.snap | 14 +- ...basic__reflectapi_struct_with_tuple-5.snap | 18 +- ...sic__reflectapi_struct_with_tuple12-5.snap | 18 +- ...__basic__reflectapi_struct_with_vec-5.snap | 14 +- ...reflectapi_struct_with_vec_external-5.snap | 14 +- ...__reflectapi_struct_with_vec_nested-5.snap | 14 +- ...sic__reflectapi_struct_with_vec_two-5.snap | 14 +- ...reflectapi_demo__tests__enums__enum-4.snap | 14 +- ...tapi_demo__tests__enums__enum_empty-4.snap | 14 +- ...demo__tests__enums__enum_rename_num-5.snap | 14 +- ...variant_and_fields_and_named_fields-4.snap | 14 +- ...num_with_discriminant_ignored_input-4.snap | 14 +- ...um_with_discriminant_ignored_output-4.snap | 14 +- ...enums__enum_with_discriminant_input-4.snap | 14 +- ...nums__enum_with_discriminant_output-4.snap | 14 +- ..._enum_with_empty_variant_and_fields-4.snap | 14 +- ...emo__tests__enums__enum_with_fields-4.snap | 14 +- ...o__tests__enums__enum_with_generics-4.snap | 14 +- ...nums__enum_with_generics_and_fields-4.snap | 14 +- ...enerics_and_fields_and_named_fields-4.snap | 14 +- ...lly_tagged_enum_with_tuple_variants-5.snap | 14 +- ...ally_tagged_enum_with_unit_variants-5.snap | 14 +- ...ics__struct_with_circular_reference-4.snap | 14 +- ...uct_with_circular_reference_generic-4.snap | 14 +- ...h_circular_reference_generic_parent-4.snap | 17 +- ...cular_reference_generic_without_box-4.snap | 14 +- ...eference_generic_without_box_parent-4.snap | 14 +- ...generic_without_box_parent_specific-4.snap | 14 +- ...__struct_with_nested_generic_struct-4.snap | 14 +- ...ct_with_nested_generic_struct_twice-4.snap | 14 +- ...enerics__struct_with_simple_generic-4.snap | 14 +- ...__generics__struct_with_vec_generic-4.snap | 14 +- ...cs__struct_with_vec_generic_generic-4.snap | 14 +- ...ct_with_vec_generic_generic_generic-4.snap | 14 +- ...adj_repr_enum_with_untagged_variant-5.snap | 14 +- ..._tests__serde__box_field_unwrapping-5.snap | 14 +- ...ectapi_demo__tests__serde__datetime-5.snap | 37 +- ...tapi_demo__tests__serde__empty_enum-5.snap | 14 +- ...e__empty_variants_adjacently_tagged-5.snap | 14 +- ...e__empty_variants_externally_tagged-5.snap | 14 +- ...e__empty_variants_internally_tagged-5.snap | 14 +- ...sts__serde__empty_variants_untagged-5.snap | 14 +- ...xed_variant_types_internally_tagged-5.snap | 14 +- ...api_demo__tests__serde__enum_rename-5.snap | 14 +- ...demo__tests__serde__enum_rename_all-5.snap | 14 +- ...__serde__enum_rename_all_on_variant-5.snap | 14 +- ...s__serde__enum_rename_variant_field-5.snap | 14 +- ...ectapi_demo__tests__serde__enum_tag-5.snap | 14 +- ...emo__tests__serde__enum_tag_content-5.snap | 14 +- ..._serde__enum_tag_content_rename_all-5.snap | 14 +- ...i_demo__tests__serde__enum_untagged-5.snap | 14 +- ..._tests__serde__enum_with_field_skip-5.snap | 14 +- ...sts__serde__enum_with_many_variants-5.snap | 14 +- ...__enum_with_rename_to_invalid_chars-5.snap | 14 +- ..._enum_with_serde_rename_on_variants-5.snap | 14 +- ...sts__serde__enum_with_variant_other-5.snap | 14 +- ...ests__serde__enum_with_variant_skip-5.snap | 14 +- ..._enum_with_variant_skip_deserialize-5.snap | 14 +- ...e__enum_with_variant_skip_serialize-5.snap | 14 +- ...__serde__enum_with_variant_untagged-5.snap | 14 +- ..._demo__tests__serde__external_impls-5.snap | 14 +- ...s__serde__field_all_python_keywords-5.snap | 14 +- ...rde__field_names_with_special_chars-5.snap | 14 +- ...latten_adjacently_tagged_enum_field-5.snap | 14 +- ...latten_enum_with_unit_variants_only-5.snap | 14 +- ...latten_externally_tagged_enum_field-5.snap | 14 +- ...s__serde__flatten_internally_tagged-5.snap | 14 +- ...latten_internally_tagged_enum_field-5.snap | 14 +- ...ts__serde__flatten_multiple_structs-5.snap | 14 +- ...ten_optional_internally_tagged_enum-5.snap | 14 +- ...n_struct_and_internal_enum_combined-5.snap | 14 +- ..._flatten_struct_with_nested_flatten-5.snap | 14 +- ...pi_demo__tests__serde__flatten_unit-5.snap | 14 +- ..._serde__flatten_untagged_enum_field-5.snap | 14 +- ...rde__generic_adjacently_tagged_enum-5.snap | 14 +- ...rde__generic_externally_tagged_enum-5.snap | 14 +- ..._generic_flatten_drops_inner_fields-5.snap | 14 +- ...eneric_flatten_enum_variant_typevar-5.snap | 14 +- ...rde__generic_flatten_leaf_collision-5.snap | 14 +- ...ests__serde__generic_flatten_nested-5.snap | 14 +- ...ts__serde__generic_flatten_optional-5.snap | 14 +- ..._generic_flatten_two_instantiations-5.snap | 14 +- ..._flatten_typevar_in_generic_context-5.snap | 14 +- ...atten_typevar_nested_in_generic_arg-5.snap | 14 +- ...ic_with_concrete_flatten_not_marked-5.snap | 14 +- ...tapi_demo__tests__serde__kebab_case-5.snap | 14 +- ...__multiple_underscore_prefix_fields-5.snap | 14 +- ...de__namespace_deeply_nested_modules-5.snap | 14 +- ...erde__namespace_single_segment_type-5.snap | 14 +- ...serde__namespace_with_numeric_start-5.snap | 14 +- ...s__serde__nested_generic_containers-5.snap | 14 +- ...rde__nested_internally_tagged_enums-5.snap | 14 +- ...ted_internally_tagged_enums_minimal-5.snap | 14 +- ..._newtype_variants_adjacently_tagged-5.snap | 14 +- ..._newtype_variants_externally_tagged-5.snap | 14 +- ..._newtype_variants_internally_tagged-5.snap | 14 +- ...emo__tests__serde__option_of_option-5.snap | 14 +- ...sts__serde__self_referential_struct-5.snap | 14 +- ...api_demo__tests__serde__struct_from-5.snap | 14 +- ...api_demo__tests__serde__struct_into-5.snap | 14 +- ...i_demo__tests__serde__struct_rename-5.snap | 14 +- ...mo__tests__serde__struct_rename_all-5.snap | 14 +- ...erde__struct_rename_all_differently-5.snap | 14 +- ...erde__struct_rename_all_pascal_case-5.snap | 14 +- ...s__serde__struct_rename_differently-5.snap | 14 +- ...__tests__serde__struct_rename_field-5.snap | 14 +- ...repr_transparent_generic_inner_type-5.snap | 14 +- ...demo__tests__serde__struct_try_from-5.snap | 14 +- ...__tests__serde__struct_with_flatten-5.snap | 14 +- ...serde__struct_with_flatten_optional-5.snap | 14 +- ..._with_flatten_optional_and_required-5.snap | 14 +- ...struct_with_rename_to_invalid_chars-5.snap | 14 +- ...e__struct_with_rename_to_kebab_case-5.snap | 14 +- ...s__serde__struct_with_serde_default-5.snap | 14 +- ...ests__serde__struct_with_serde_skip-5.snap | 14 +- ..._struct_with_serde_skip_deserialize-5.snap | 14 +- ...e__struct_with_serde_skip_serialize-5.snap | 14 +- ...struct_with_serde_skip_serialize_if-5.snap | 14 +- ...ectapi_demo__tests__serde__timezone-5.snap | 14 +- ...ctapi_demo__tests__write_openapi_spec.snap | 172 ++ reflectapi-python-runtime/.coverage | Bin 0 -> 53248 bytes reflectapi-python-runtime/coverage.xml | 1595 +++++++++++++++++ ...flectapi_runtime-0.17.2a1-py3-none-any.whl | Bin 0 -> 34048 bytes .../dist/reflectapi_runtime-0.17.2a1.tar.gz | Bin 0 -> 60036 bytes .../src/reflectapi_runtime/__init__.py | 2 + .../src/reflectapi_runtime/duration.py | 73 + .../tests/test_codegen_regressions.py | 90 + reflectapi-schema/src/lib.rs | 11 + reflectapi/src/codegen/python.rs | 206 ++- 183 files changed, 5112 insertions(+), 376 deletions(-) create mode 100644 reflectapi-demo/clients/python/api_client/myapi/order/__init__.py create mode 100644 reflectapi-demo/clients/python/api_client/std/__init__.py create mode 100644 reflectapi-demo/clients/python/api_client/std/time/__init__.py create mode 100644 reflectapi-python-runtime/.coverage create mode 100644 reflectapi-python-runtime/coverage.xml create mode 100644 reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1-py3-none-any.whl create mode 100644 reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1.tar.gz create mode 100644 reflectapi-python-runtime/src/reflectapi_runtime/duration.py create mode 100644 reflectapi-python-runtime/tests/test_codegen_regressions.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bf9aecb0..7e9a2448 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -38,3 +38,30 @@ jobs: - name: Run tests working-directory: reflectapi-python-runtime run: uv run --extra dev pytest -q --no-cov + + # Regenerates the demo Python client from the in-repo schema and + # imports it. The generated package's strict `_rebuild_models()` runs + # at import time and raises on any dangling type reference (the class + # of bug that produced four user-reported issues in 0.17.x: + # namespace-alias mismatches, std.tuple/std.marker references with no + # backing class, etc.). If this job is green the whole class is gone; + # if it ever goes red, the codegen has regressed. + python-codegen-smoke: + name: Python codegen smoke test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + - uses: astral-sh/setup-uv@v3 + - name: Regenerate demo Python client from schema + run: | + cargo run -p reflectapi-cli --quiet -- codegen \ + -s reflectapi-demo/reflectapi.json \ + -o reflectapi-demo/clients/python/api_client \ + -l python --python-package-name api_client --python-sync -f + - name: Import demo client (triggers strict model_rebuild) + working-directory: reflectapi-demo/clients/python + run: uv run python -c "import api_client; print('demo client import OK')" + - name: Snapshot is committed (no drift) + run: git diff --exit-code reflectapi-demo/clients/python/api_client/ diff --git a/reflectapi-demo/clients/python/api_client/_client.py b/reflectapi-demo/clients/python/api_client/_client.py index 1e3460a2..682f13f2 100644 --- a/reflectapi-demo/clients/python/api_client/_client.py +++ b/reflectapi-demo/clients/python/api_client/_client.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") @@ -274,6 +278,28 @@ def __init__( self.pets = AsyncPetsClient(self) + async def codegen_regression( + self, + data: Optional[myapi.CodegenRegressionRequest] = None, + ) -> ApiResponse[myapi.CodegenRegressionResponse]: + """Regression-test endpoint pinning codegen bugs + + Args: + data: Request data for the codegen_regression operation. + + Returns: + ApiResponse[myapi.CodegenRegressionResponse]: Response containing myapi.CodegenRegressionResponse data + """ + path = "/codegen-regression" + + params: dict[str, Any] = {} + return await self._make_request( + path, + params=params if params else None, + json_model=data, + response_model=myapi.CodegenRegressionResponse, + ) + class HealthClient: """Synchronous client for health operations.""" @@ -497,3 +523,25 @@ def __init__( self.health = HealthClient(self) self.pets = PetsClient(self) + + def codegen_regression( + self, + data: Optional[myapi.CodegenRegressionRequest] = None, + ) -> ApiResponse[myapi.CodegenRegressionResponse]: + """Regression-test endpoint pinning codegen bugs + + Args: + data: Request data for the codegen_regression operation. + + Returns: + ApiResponse[myapi.CodegenRegressionResponse]: Response containing myapi.CodegenRegressionResponse data + """ + path = "/codegen-regression" + + params: dict[str, Any] = {} + return self._make_request( + path, + params=params if params else None, + json_model=data, + response_model=myapi.CodegenRegressionResponse, + ) diff --git a/reflectapi-demo/clients/python/api_client/_rebuild.py b/reflectapi-demo/clients/python/api_client/_rebuild.py index a53eacec..a00559d9 100644 --- a/reflectapi-demo/clients/python/api_client/_rebuild.py +++ b/reflectapi-demo/clients/python/api_client/_rebuild.py @@ -11,7 +11,11 @@ from . import myapi -from .myapi import MyapiHealthCheckFail +from .myapi import ( + MyapiCodegenRegressionRequest, + MyapiCodegenRegressionResponse, + MyapiHealthCheckFail, +) from . import myapi @@ -35,6 +39,10 @@ from . import myapi +from .myapi.order import MyapiOrderInsertData, MyapiOrderPolicy, MyapiOrderRateLimit + +from . import myapi + from .myapi.proto import ( MyapiProtoHeaders, MyapiProtoInternalError, @@ -71,14 +79,21 @@ def rebuild_models() -> None: myapi.model.myapi = myapi myapi.model.input.myapi = myapi myapi.model.output.myapi = myapi + myapi.order.myapi = myapi myapi.proto.myapi = myapi reflectapi.reflectapi = reflectapi + errors: list[str] = [] for _model in [ + MyapiCodegenRegressionRequest, + MyapiCodegenRegressionResponse, MyapiHealthCheckFail, MyapiModelBehavior, MyapiModelKind, MyapiModelInputPet, MyapiModelOutputPet, + MyapiOrderInsertData, + MyapiOrderPolicy, + MyapiOrderRateLimit, MyapiProtoHeaders, MyapiProtoInternalError, MyapiProtoPaginated, @@ -92,7 +107,16 @@ def rebuild_models() -> None: MyapiProtoValidationA, MyapiProtoValidationError, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as exc: + errors.append(f" - {_model.__name__}: {type(exc).__name__}: {exc}") + if errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(errors) + ) diff --git a/reflectapi-demo/clients/python/api_client/generated.py b/reflectapi-demo/clients/python/api_client/generated.py index 749061b1..d58bee2e 100644 --- a/reflectapi-demo/clients/python/api_client/generated.py +++ b/reflectapi-demo/clients/python/api_client/generated.py @@ -15,7 +15,11 @@ from . import myapi -from .myapi import MyapiHealthCheckFail +from .myapi import ( + MyapiCodegenRegressionRequest, + MyapiCodegenRegressionResponse, + MyapiHealthCheckFail, +) from . import myapi @@ -39,6 +43,10 @@ from . import myapi +from .myapi.order import MyapiOrderInsertData, MyapiOrderPolicy, MyapiOrderRateLimit + +from . import myapi + from .myapi.proto import ( MyapiProtoHeaders, MyapiProtoInternalError, @@ -76,6 +84,8 @@ __all__ = [ "AsyncClient", "Client", + "MyapiCodegenRegressionRequest", + "MyapiCodegenRegressionResponse", "MyapiHealthCheckFail", "MyapiModelBehavior", "MyapiModelBehaviorAggressiveVariant", @@ -86,6 +96,9 @@ "MyapiModelKindCat", "MyapiModelKindDog", "MyapiModelOutputPet", + "MyapiOrderInsertData", + "MyapiOrderPolicy", + "MyapiOrderRateLimit", "MyapiProtoHeaders", "MyapiProtoInternalError", "MyapiProtoPaginated", diff --git a/reflectapi-demo/clients/python/api_client/myapi/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/__init__.py index decc0a23..df8f0ec1 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/__init__.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") @@ -49,21 +53,54 @@ StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] +class MyapiCodegenRegressionRequest(BaseModel): + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + order: myapi.order.OrderInsertData = Field( + description="Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability." + ) + rate_limit: myapi.order.RateLimit = Field( + description="Pulls in `order::RateLimit` for Duration (bug 3)." + ) + policy: myapi.order.Policy[str, int] = Field( + description="Pulls in `order::Policy` for PhantomData (bug 4)." + ) + + +class MyapiCodegenRegressionResponse(BaseModel): + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + ok: bool + + class MyapiHealthCheckFail(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) # Public aliases for this module +CodegenRegressionRequest = MyapiCodegenRegressionRequest +CodegenRegressionResponse = MyapiCodegenRegressionResponse HealthCheckFail = MyapiHealthCheckFail from . import model +from . import order from . import proto try: from .._rebuild import rebuild_models as _rebuild_models _rebuild_models() -except Exception: +except ImportError: pass -__all__ = ["HealthCheckFail", "MyapiHealthCheckFail", "model", "proto"] +__all__ = [ + "CodegenRegressionRequest", + "CodegenRegressionResponse", + "HealthCheckFail", + "MyapiCodegenRegressionRequest", + "MyapiCodegenRegressionResponse", + "MyapiHealthCheckFail", + "model", + "order", + "proto", +] diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py index 1c328c31..60625491 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py index e1bd042e..e4f14188 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py index 7226c6f4..4d17e818 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py new file mode 100644 index 00000000..1b690887 --- /dev/null +++ b/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py @@ -0,0 +1,109 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Demo application +This is a demo application +""" + +from __future__ import annotations + + +# Standard library imports +import warnings +from collections.abc import AsyncIterator, Iterator +from datetime import datetime +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import ( + BaseModel, + ConfigDict, + Field, + RootModel, + model_serializer, + model_validator, +) + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel +from reflectapi_runtime import ( + parse_externally_tagged as _parse_externally_tagged, + serialize_externally_tagged as _serialize_externally_tagged, +) + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class MyapiOrderInsertData(BaseModel): + """Bug 1 (namespace alias mismatch). The struct name starts with + the parent namespace\\'s cap (`Order…`) — the alias-stripping + pass used to strip the leading cap from the namespace alias + (`order.InsertData = OrderInsertData`) while field annotations + kept the un-stripped form, producing `order.OrderInsertData` + which doesn\\'t resolve at `model_rebuild()` time.""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + identity: str + alternative_part_number: tuple[str, str] | None = Field( + default=None, + description="Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + ) + + +class MyapiOrderPolicy(BaseModel, Generic[C, T]): + """Bug 4 (PhantomData). PhantomData has no wire data — serde + skips it. The codegen used to emit `std.marker.PhantomData[T]` + as a field annotation, leaving a dangling reference.""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + name: str + + +class MyapiOrderRateLimit(BaseModel): + """Bug 3 (Duration shape). serde emits `Duration` as + `{\\"secs\\": , \\"nanos\\": }`. Pydantic\\'s `timedelta` + validator rejects that shape, so any response with a Duration + failed validation.""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + retry_after: ReflectapiDuration + max_wait: ReflectapiDuration | None = None + + +# Public aliases for this module +InsertData = MyapiOrderInsertData +OrderInsertData = MyapiOrderInsertData +Policy = MyapiOrderPolicy +RateLimit = MyapiOrderRateLimit + +__all__ = [ + "InsertData", + "MyapiOrderInsertData", + "MyapiOrderPolicy", + "MyapiOrderRateLimit", + "OrderInsertData", + "Policy", + "RateLimit", +] diff --git a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py index 5d025583..8c808cbe 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py index 5326c6c0..f73a18b9 100644 --- a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py +++ b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py @@ -28,7 +28,9 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -39,6 +41,8 @@ # Type variables for generic types +C = TypeVar("C") + T = TypeVar("T") @@ -85,7 +89,7 @@ class ReflectapiOptionSome(BaseModel): from .._rebuild import rebuild_models as _rebuild_models _rebuild_models() -except Exception: +except ImportError: pass __all__ = [ diff --git a/reflectapi-demo/clients/python/api_client/std/__init__.py b/reflectapi-demo/clients/python/api_client/std/__init__.py new file mode 100644 index 00000000..ad318f5a --- /dev/null +++ b/reflectapi-demo/clients/python/api_client/std/__init__.py @@ -0,0 +1,64 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Demo application +This is a demo application +""" + +from __future__ import annotations + + +# Standard library imports +import warnings +from collections.abc import AsyncIterator, Iterator +from datetime import datetime, timedelta +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import ( + BaseModel, + ConfigDict, + Field, + RootModel, + model_serializer, + model_validator, +) + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel +from reflectapi_runtime import ( + parse_externally_tagged as _parse_externally_tagged, + serialize_externally_tagged as _serialize_externally_tagged, +) + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +from . import time + +try: + from .._rebuild import rebuild_models as _rebuild_models + + _rebuild_models() +except Exception: + pass + +__all__ = ["time"] diff --git a/reflectapi-demo/clients/python/api_client/std/time/__init__.py b/reflectapi-demo/clients/python/api_client/std/time/__init__.py new file mode 100644 index 00000000..30d24dc5 --- /dev/null +++ b/reflectapi-demo/clients/python/api_client/std/time/__init__.py @@ -0,0 +1,67 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Demo application +This is a demo application +""" + +from __future__ import annotations + + +# Standard library imports +import warnings +from collections.abc import AsyncIterator, Iterator +from datetime import datetime, timedelta +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import ( + BaseModel, + ConfigDict, + Field, + RootModel, + model_serializer, + model_validator, +) + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel +from reflectapi_runtime import ( + parse_externally_tagged as _parse_externally_tagged, + serialize_externally_tagged as _serialize_externally_tagged, +) + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class StdTimeDuration(BaseModel): + """Time duration type""" + + model_config = ConfigDict(extra="ignore", populate_by_name=True) + + secs: int + nanos: int + + +# Public aliases for this module +Duration = StdTimeDuration + +__all__ = ["Duration", "StdTimeDuration"] diff --git a/reflectapi-demo/clients/rust/generated/src/generated.rs b/reflectapi-demo/clients/rust/generated/src/generated.rs index cbb53d4b..fb376ee0 100644 --- a/reflectapi-demo/clients/rust/generated/src/generated.rs +++ b/reflectapi-demo/clients/rust/generated/src/generated.rs @@ -27,6 +27,19 @@ pub mod interface { client, } } + /// Regression-test endpoint pinning codegen bugs + #[tracing::instrument(name = "/codegen-regression", skip(self, headers))] + pub async fn codegen_regression( + &self, + input: super::types::myapi::CodegenRegressionRequest, + headers: reflectapi::Empty, + ) -> Result< + super::types::myapi::CodegenRegressionResponse, + reflectapi::rt::Error, + > { + reflectapi::rt::__request_impl(&self.client, "/codegen-regression", input, headers) + .await + } } #[cfg(feature = "reqwest")] @@ -172,6 +185,22 @@ pub mod interface { pub mod types { pub mod myapi { + #[derive(Debug, serde::Serialize)] + pub struct CodegenRegressionRequest { + /// Pulls in `order::OrderInsertData` (bug 1) and Rust tuple + /// (bug 2) reachability. + pub order: super::myapi::order::OrderInsertData, + /// Pulls in `order::RateLimit` for Duration (bug 3). + pub rate_limit: super::myapi::order::RateLimit, + /// Pulls in `order::Policy` for PhantomData (bug 4). + pub policy: super::myapi::order::Policy, + } + + #[derive(Debug, serde::Deserialize)] + pub struct CodegenRegressionResponse { + pub ok: bool, + } + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct HealthCheckFail {} @@ -277,6 +306,44 @@ pub mod types { } } } + pub mod order { + + /// Bug 1 (namespace alias mismatch). The struct name starts with + /// the parent namespace\'s cap (`Order…`) — the alias-stripping + /// pass used to strip the leading cap from the namespace alias + /// (`order.InsertData = OrderInsertData`) while field annotations + /// kept the un-stripped form, producing `order.OrderInsertData` + /// which doesn\'t resolve at `model_rebuild()` time. + #[derive(Debug, serde::Serialize)] + pub struct OrderInsertData { + pub identity: std::string::String, + /// Bug 2 (tuple types). serde emits Rust tuples as JSON arrays; + /// the Python codegen used to emit `std.tuple.Tuple2[...]` + /// with no matching class, so any reference broke at rebuild. + pub alternative_part_number: + std::option::Option<(std::string::String, std::string::String)>, + } + + /// Bug 4 (PhantomData). PhantomData has no wire data — serde + /// skips it. The codegen used to emit `std.marker.PhantomData[T]` + /// as a field annotation, leaving a dangling reference. + #[derive(Debug, serde::Serialize)] + pub struct Policy { + pub name: std::string::String, + pub _context_marker: std::marker::PhantomData, + pub _output_marker: std::marker::PhantomData, + } + + /// Bug 3 (Duration shape). serde emits `Duration` as + /// `{\"secs\": , \"nanos\": }`. Pydantic\'s `timedelta` + /// validator rejects that shape, so any response with a Duration + /// failed validation. + #[derive(Debug, serde::Serialize)] + pub struct RateLimit { + pub retry_after: std::time::Duration, + pub max_wait: std::option::Option, + } + } pub mod proto { #[derive(Debug, serde::Serialize)] diff --git a/reflectapi-demo/clients/typescript/generated.ts b/reflectapi-demo/clients/typescript/generated.ts index ba2c265a..7dc7a086 100644 --- a/reflectapi-demo/clients/typescript/generated.ts +++ b/reflectapi-demo/clients/typescript/generated.ts @@ -634,6 +634,14 @@ class __EventSourceParserStream extends TransformStream< export namespace __definition { export interface Interface { + /** + * Regression-test endpoint pinning codegen bugs + */ + codegen_regression: ( + input: myapi.CodegenRegressionRequest, + headers: {}, + options?: RequestOptions, + ) => AsyncResult; health: HealthInterface; pets: PetsInterface; } @@ -719,6 +727,26 @@ export namespace __definition { } } export namespace myapi { + export interface CodegenRegressionRequest { + /** + * Pulls in `order::OrderInsertData` (bug 1) and Rust tuple + * (bug 2) reachability. + */ + order: myapi.order.OrderInsertData; + /** + * Pulls in `order::RateLimit` for Duration (bug 3). + */ + rate_limit: myapi.order.RateLimit; + /** + * Pulls in `order::Policy` for PhantomData (bug 4). + */ + policy: myapi.order.Policy; + } + + export interface CodegenRegressionResponse { + ok: boolean; + } + export interface HealthCheckFail {} export namespace model { @@ -823,6 +851,48 @@ export namespace myapi { } } + export namespace order { + /** + * Bug 1 (namespace alias mismatch). The struct name starts with + * the parent namespace\'s cap (`Order…`) — the alias-stripping + * pass used to strip the leading cap from the namespace alias + * (`order.InsertData = OrderInsertData`) while field annotations + * kept the un-stripped form, producing `order.OrderInsertData` + * which doesn\'t resolve at `model_rebuild()` time. + */ + export interface OrderInsertData { + identity: string; + /** + * Bug 2 (tuple types). serde emits Rust tuples as JSON arrays; + * the Python codegen used to emit `std.tuple.Tuple2[...]` + * with no matching class, so any reference broke at rebuild. + */ + alternative_part_number: [string, string] | null; + } + + /** + * Bug 4 (PhantomData). PhantomData has no wire data — serde + * skips it. The codegen used to emit `std.marker.PhantomData[T]` + * as a field annotation, leaving a dangling reference. + */ + export interface Policy { + name: string; + _context_marker: undefined | C /* phantom data */; + _output_marker: undefined | T /* phantom data */; + } + + /** + * Bug 3 (Duration shape). serde emits `Duration` as + * `{\"secs\": , \"nanos\": }`. Pydantic\'s `timedelta` + * validator rejects that shape, so any response with a Duration + * failed validation. + */ + export interface RateLimit { + retry_after: std.time.Duration; + max_wait: std.time.Duration | null; + } + } + export namespace proto { export interface Headers { /** @@ -919,6 +989,23 @@ export namespace reflectapi { * Struct object with no fields */ export interface Empty {} + + /** + * Error object which is expected to be never returned + */ + export interface Infallible {} +} + +export namespace std { + export namespace time { + /** + * Time duration type + */ + export interface Duration { + secs: number /* u64 */; + nanos: number /* u32 */; + } + } } namespace __implementation { @@ -929,6 +1016,7 @@ namespace __implementation { typeof base === "string" ? new ClientInstance(base) : base; return { impl: { + codegen_regression: codegen_regression(client_instance), health: { check: health__check(client_instance), }, @@ -1048,4 +1136,17 @@ namespace __implementation { myapi.proto.UnauthorizedError >(client, "/pets.cdc-events", input, headers, options); } + function codegen_regression(client: Client) { + return ( + input: myapi.CodegenRegressionRequest, + headers: {}, + options?: RequestOptions, + ) => + __request< + myapi.CodegenRegressionRequest, + {}, + myapi.CodegenRegressionResponse, + {} + >(client, "/codegen-regression", input, headers, options); + } } diff --git a/reflectapi-demo/openapi.json b/reflectapi-demo/openapi.json index 3a31792d..0a4b3568 100644 --- a/reflectapi-demo/openapi.json +++ b/reflectapi-demo/openapi.json @@ -6,6 +6,35 @@ "version": "1.0.0" }, "paths": { + "/codegen-regression": { + "description": "Regression-test endpoint pinning codegen bugs", + "post": { + "operationId": "codegen-regression", + "description": "Regression-test endpoint pinning codegen bugs", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.CodegenRegressionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.CodegenRegressionResponse" + } + } + } + } + } + } + }, "/pets.cdc-events": { "description": "Stream of change data capture events for pets", "post": { @@ -381,10 +410,68 @@ }, "components": { "schemas": { + "bool": { + "description": "Boolean value", + "type": "boolean" + }, "f64": { "description": "64-bit floating point number", "type": "number" }, + "myapi.CodegenRegressionRequest": { + "type": "object", + "title": "myapi.CodegenRegressionRequest", + "required": [ + "order", + "policy", + "rate_limit" + ], + "properties": { + "order": { + "description": "Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability.", + "$ref": "#/components/schemas/myapi.order.OrderInsertData" + }, + "policy": { + "description": "Pulls in `order::Policy` for PhantomData (bug 4).", + "type": "object", + "title": "myapi.order.Policy", + "required": [ + "_context_marker", + "_output_marker", + "name" + ], + "properties": { + "_context_marker": { + "description": "Zero-sized phantom data", + "type": "null" + }, + "_output_marker": { + "description": "Zero-sized phantom data", + "type": "null" + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "rate_limit": { + "description": "Pulls in `order::RateLimit` for Duration (bug 3).", + "$ref": "#/components/schemas/myapi.order.RateLimit" + } + } + }, + "myapi.CodegenRegressionResponse": { + "type": "object", + "title": "myapi.CodegenRegressionResponse", + "required": [ + "ok" + ], + "properties": { + "ok": { + "$ref": "#/components/schemas/bool" + } + } + }, "myapi.model.Behavior": { "oneOf": [ { @@ -577,6 +664,66 @@ } } }, + "myapi.order.OrderInsertData": { + "description": "Bug 1 (namespace alias mismatch). The struct name starts with\nthe parent namespace\\'s cap (`Order…`) — the alias-stripping\npass used to strip the leading cap from the namespace alias\n(`order.InsertData = OrderInsertData`) while field annotations\nkept the un-stripped form, producing `order.OrderInsertData`\nwhich doesn\\'t resolve at `model_rebuild()` time.", + "type": "object", + "title": "myapi.order.OrderInsertData", + "required": [ + "alternative_part_number", + "identity" + ], + "properties": { + "alternative_part_number": { + "description": "Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "description": "Tuple holding 2 elements", + "type": "array", + "prefixItems": [ + { + "$ref": "#/components/schemas/std.string.String" + }, + { + "$ref": "#/components/schemas/std.string.String" + } + ] + } + ] + }, + "identity": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.order.RateLimit": { + "description": "Bug 3 (Duration shape). serde emits `Duration` as\n`{\\\"secs\\\": , \\\"nanos\\\": }`. Pydantic\\'s `timedelta`\nvalidator rejects that shape, so any response with a Duration\nfailed validation.", + "type": "object", + "title": "myapi.order.RateLimit", + "required": [ + "max_wait", + "retry_after" + ], + "properties": { + "max_wait": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/std.time.Duration" + } + ] + }, + "retry_after": { + "$ref": "#/components/schemas/std.time.Duration" + } + } + }, "myapi.proto.InternalError": { "type": "object", "title": "myapi.proto.InternalError", @@ -842,10 +989,35 @@ "description": "UTF-8 encoded string", "type": "string" }, + "std.time.Duration": { + "description": "Time duration type", + "type": "object", + "title": "std.time.Duration", + "required": [ + "nanos", + "secs" + ], + "properties": { + "nanos": { + "$ref": "#/components/schemas/u32" + }, + "secs": { + "$ref": "#/components/schemas/u64" + } + } + }, "std.tuple.Tuple0": { "description": "Unit type", "type": "null" }, + "u32": { + "description": "32-bit unsigned integer", + "type": "integer" + }, + "u64": { + "description": "64-bit unsigned integer", + "type": "integer" + }, "u8": { "description": "8-bit unsigned integer", "type": "integer" diff --git a/reflectapi-demo/reflectapi.json b/reflectapi-demo/reflectapi.json index f014da8b..39542941 100644 --- a/reflectapi-demo/reflectapi.json +++ b/reflectapi-demo/reflectapi.json @@ -167,6 +167,22 @@ "msgpack" ], "readonly": true + }, + { + "name": "codegen-regression", + "path": "", + "description": "Regression-test endpoint pinning codegen bugs", + "input_type": { + "name": "myapi::CodegenRegressionRequest" + }, + "output_kind": "complete", + "output_type": { + "name": "myapi::CodegenRegressionResponse" + }, + "serialization": [ + "json", + "msgpack" + ] } ], "input_types": { @@ -189,6 +205,46 @@ "name": "f64", "description": "64-bit floating point number" }, + { + "kind": "struct", + "name": "myapi::CodegenRegressionRequest", + "fields": { + "named": [ + { + "name": "order", + "description": "Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability.", + "type": { + "name": "myapi::order::OrderInsertData" + }, + "required": true + }, + { + "name": "rate_limit", + "description": "Pulls in `order::RateLimit` for Duration (bug 3).", + "type": { + "name": "myapi::order::RateLimit" + }, + "required": true + }, + { + "name": "policy", + "description": "Pulls in `order::Policy` for PhantomData (bug 4).", + "type": { + "name": "myapi::order::Policy", + "arguments": [ + { + "name": "std::string::String" + }, + { + "name": "u32" + } + ] + }, + "required": true + } + ] + } + }, { "kind": "enum", "name": "myapi::model::Behavior", @@ -352,6 +408,119 @@ ] } }, + { + "kind": "struct", + "name": "myapi::order::OrderInsertData", + "description": "Bug 1 (namespace alias mismatch). The struct name starts with\nthe parent namespace\\'s cap (`Order…`) — the alias-stripping\npass used to strip the leading cap from the namespace alias\n(`order.InsertData = OrderInsertData`) while field annotations\nkept the un-stripped form, producing `order.OrderInsertData`\nwhich doesn\\'t resolve at `model_rebuild()` time.", + "fields": { + "named": [ + { + "name": "identity", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "alternative_part_number", + "description": "Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + "type": { + "name": "std::option::Option", + "arguments": [ + { + "name": "std::tuple::Tuple2", + "arguments": [ + { + "name": "std::string::String" + }, + { + "name": "std::string::String" + } + ] + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::order::Policy", + "description": "Bug 4 (PhantomData). PhantomData has no wire data — serde\nskips it. The codegen used to emit `std.marker.PhantomData[T]`\nas a field annotation, leaving a dangling reference.", + "parameters": [ + { + "name": "C" + }, + { + "name": "T" + } + ], + "fields": { + "named": [ + { + "name": "name", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "_context_marker", + "type": { + "name": "std::marker::PhantomData", + "arguments": [ + { + "name": "C" + } + ] + }, + "required": true + }, + { + "name": "_output_marker", + "type": { + "name": "std::marker::PhantomData", + "arguments": [ + { + "name": "T" + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::order::RateLimit", + "description": "Bug 3 (Duration shape). serde emits `Duration` as\n`{\\\"secs\\\": , \\\"nanos\\\": }`. Pydantic\\'s `timedelta`\nvalidator rejects that shape, so any response with a Duration\nfailed validation.", + "fields": { + "named": [ + { + "name": "retry_after", + "type": { + "name": "std::time::Duration" + }, + "required": true + }, + { + "name": "max_wait", + "type": { + "name": "std::option::Option", + "arguments": [ + { + "name": "std::time::Duration" + } + ] + }, + "required": true + } + ] + } + }, { "kind": "struct", "name": "myapi::proto::Headers", @@ -529,6 +698,16 @@ } ] }, + { + "kind": "primitive", + "name": "std::marker::PhantomData", + "description": "Zero-sized phantom data", + "parameters": [ + { + "name": "T" + } + ] + }, { "kind": "enum", "name": "std::option::Option", @@ -566,6 +745,42 @@ "name": "std::string::String", "description": "UTF-8 encoded string" }, + { + "kind": "struct", + "name": "std::time::Duration", + "description": "Time duration type", + "fields": { + "named": [ + { + "name": "secs", + "type": { + "name": "u64" + }, + "required": true + }, + { + "name": "nanos", + "type": { + "name": "u32" + }, + "required": true + } + ] + } + }, + { + "kind": "primitive", + "name": "std::tuple::Tuple2", + "description": "Tuple holding 2 elements", + "parameters": [ + { + "name": "T1" + }, + { + "name": "T2" + } + ] + }, { "kind": "primitive", "name": "std::vec::Vec", @@ -576,6 +791,16 @@ } ] }, + { + "kind": "primitive", + "name": "u32", + "description": "32-bit unsigned integer" + }, + { + "kind": "primitive", + "name": "u64", + "description": "64-bit unsigned integer" + }, { "kind": "primitive", "name": "u8", @@ -585,6 +810,11 @@ }, "output_types": { "types": [ + { + "kind": "primitive", + "name": "bool", + "description": "Boolean value" + }, { "kind": "primitive", "name": "chrono::DateTime", @@ -603,6 +833,21 @@ "name": "f64", "description": "64-bit floating point number" }, + { + "kind": "struct", + "name": "myapi::CodegenRegressionResponse", + "fields": { + "named": [ + { + "name": "ok", + "type": { + "name": "bool" + }, + "required": true + } + ] + } + }, { "kind": "struct", "name": "myapi::HealthCheckFail", @@ -990,6 +1235,12 @@ "description": "Struct object with no fields", "fields": "none" }, + { + "kind": "struct", + "name": "reflectapi::Infallible", + "description": "Error object which is expected to be never returned", + "fields": "none" + }, { "kind": "enum", "name": "std::option::Option", diff --git a/reflectapi-demo/src/lib.rs b/reflectapi-demo/src/lib.rs index 9b4e54a0..58f589f0 100644 --- a/reflectapi-demo/src/lib.rs +++ b/reflectapi-demo/src/lib.rs @@ -49,6 +49,10 @@ pub fn builder() -> reflectapi::Builder> { .readonly(true) .description("Stream of change data capture events for pets") }) + .route(codegen_regression, |b| { + b.name("codegen-regression") + .description("Regression-test endpoint pinning codegen bugs") + }) .rename_types("reflectapi_demo::", "myapi::") // and some optional linting rules .validate(|schema| { @@ -83,6 +87,34 @@ async fn health_check( Ok(reflectapi::Empty {}) } +#[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] +pub struct CodegenRegressionRequest { + /// Pulls in `order::OrderInsertData` (bug 1) and Rust tuple + /// (bug 2) reachability. + pub order: order::OrderInsertData, + /// Pulls in `order::RateLimit` for Duration (bug 3). + pub rate_limit: order::RateLimit, + /// Pulls in `order::Policy` for PhantomData (bug 4). + pub policy: order::Policy, +} + +#[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] +pub struct CodegenRegressionResponse { + pub ok: bool, +} + +/// Pure regression-test endpoint. Doesn't run in the demo server — it +/// only exists so the four bug-pinning types appear in the generated +/// schema and the Python codegen has to render them. +async fn codegen_regression( + _: Arc, + request: CodegenRegressionRequest, + _headers: reflectapi::Empty, +) -> Result { + let _ = request; + Ok(CodegenRegressionResponse { ok: true }) +} + #[derive(Debug)] pub struct AppState { pets: Mutex>, @@ -98,6 +130,63 @@ impl Default for AppState { } } +// Regression-test module: every type here exists to exercise a +// previously-broken Python codegen path. Each comment block links the +// type to the bug it pins. +mod order { + use std::marker::PhantomData; + use std::time::Duration; + + /// Bug 1 (namespace alias mismatch). The struct name starts with + /// the parent namespace's cap (`Order…`) — the alias-stripping + /// pass used to strip the leading cap from the namespace alias + /// (`order.InsertData = OrderInsertData`) while field annotations + /// kept the un-stripped form, producing `order.OrderInsertData` + /// which doesn't resolve at `model_rebuild()` time. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct OrderInsertData { + pub identity: String, + /// Bug 2 (tuple types). serde emits Rust tuples as JSON arrays; + /// the Python codegen used to emit `std.tuple.Tuple2[...]` + /// with no matching class, so any reference broke at rebuild. + pub alternative_part_number: Option<(String, String)>, + } + + /// Bug 3 (Duration shape). serde emits `Duration` as + /// `{"secs": , "nanos": }`. Pydantic's `timedelta` + /// validator rejects that shape, so any response with a Duration + /// failed validation. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct RateLimit { + pub retry_after: Duration, + pub max_wait: Option, + } + + /// Bug 4 (PhantomData). PhantomData has no wire data — serde + /// skips it. The codegen used to emit `std.marker.PhantomData[T]` + /// as a field annotation, leaving a dangling reference. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct Policy + where + C: 'static, + T: 'static, + { + pub name: String, + // No `#[serde(skip)]` here — serde emits PhantomData as JSON + // `null`, and reflectapi-derive surfaces the field in the + // schema. The Python codegen used to render it as + // `std.marker.PhantomData[T]`, which broke `model_rebuild()`. + pub _context_marker: PhantomData, + pub _output_marker: PhantomData, + } +} + mod model { #[derive( Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap index 835fb843..e4555685 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap @@ -138,10 +138,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicStructWithDeprecatedField, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap index 4cba3b9c..1d9c75ab 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap @@ -227,10 +227,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestEnumDocumented, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_with_skip_variant-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_with_skip_variant-5.snap index 629a048e..ecd605c4 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_with_skip_variant-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_with_skip_variant-5.snap @@ -155,11 +155,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicInputTestEnumWithSkipVariant, ReflectapiDemoTestsBasicOutputTestEnumWithSkipVariant, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap index ff325399..d5d54637 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap @@ -138,10 +138,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructDocumented, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap index 3af36d01..63ddd590 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap @@ -132,10 +132,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructEmpty, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap index 0de8e42a..c40e67c6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap @@ -126,10 +126,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOneBasicFieldStaticStr, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap index 0df0886f..3706dc15 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap @@ -140,10 +140,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOneBasicFieldString, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap index baee3478..3bafdf41 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap @@ -144,10 +144,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOneBasicFieldStringReflectBoth, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap index ddbb5bcb..f0f62191 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap @@ -146,10 +146,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOneBasicFieldStringReflectBothEqually, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap index 5b680414..300c6d0f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap @@ -142,10 +142,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOneBasicFieldStringReflectBothEqually, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap index 3d92d096..9ab7ad33 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap @@ -160,11 +160,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicInputTestStructOneBasicFieldStringReflectBothDifferently, ReflectapiDemoTestsBasicOutputTestStructOneBasicFieldStringReflectBothD_afce1cc8, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap index afd7f05f..c0b151a7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOneBasicFieldU32, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap index c9bcde61..8a4d609e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap @@ -135,10 +135,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructOption, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap index 9ab81184..9af91573 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap @@ -137,10 +137,17 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructTuple, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + str(len(_rebuild_errors)) + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap index d19d1727..ee1adbd0 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap @@ -147,12 +147,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTest, ReflectapiDemoTestsBasicX, ReflectapiDemoTestsBasicY, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap index 7821b150..85c676c6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap @@ -56,39 +56,39 @@ class ReflectapiDemoTestsBasicTestStructWithAllPrimitiveTypeFields(BaseModel): f_hashset: bytes = Field( serialization_alias="_f_hashset", validation_alias="_f_hashset" ) - f_tuple: std.tuple.Tuple2[int, str] = Field( + f_tuple: tuple[int, str] = Field( serialization_alias="_f_tuple", validation_alias="_f_tuple" ) - f_tuple3: std.tuple.Tuple3[int, str, int] = Field( + f_tuple3: tuple[int, str, int] = Field( serialization_alias="_f_tuple3", validation_alias="_f_tuple3" ) - f_tuple4: std.tuple.Tuple4[int, str, int, str] = Field( + f_tuple4: tuple[int, str, int, str] = Field( serialization_alias="_f_tuple4", validation_alias="_f_tuple4" ) - f_tuple5: std.tuple.Tuple5[int, str, int, str, int] = Field( + f_tuple5: tuple[int, str, int, str, int] = Field( serialization_alias="_f_tuple5", validation_alias="_f_tuple5" ) - f_tuple6: std.tuple.Tuple6[int, str, int, str, int, str] = Field( + f_tuple6: tuple[int, str, int, str, int, str] = Field( serialization_alias="_f_tuple6", validation_alias="_f_tuple6" ) - f_tuple7: std.tuple.Tuple7[int, str, int, str, int, str, int] = Field( + f_tuple7: tuple[int, str, int, str, int, str, int] = Field( serialization_alias="_f_tuple7", validation_alias="_f_tuple7" ) - f_tuple8: std.tuple.Tuple8[int, str, int, str, int, str, int, str] = Field( + f_tuple8: tuple[int, str, int, str, int, str, int, str] = Field( serialization_alias="_f_tuple8", validation_alias="_f_tuple8" ) - f_tuple9: std.tuple.Tuple9[int, str, int, str, int, str, int, str, int] = Field( + f_tuple9: tuple[int, str, int, str, int, str, int, str, int] = Field( serialization_alias="_f_tuple9", validation_alias="_f_tuple9" ) - f_tuple10: std.tuple.Tuple10[int, str, int, str, int, str, int, str, int, str] = ( - Field(serialization_alias="_f_tuple10", validation_alias="_f_tuple10") + f_tuple10: tuple[int, str, int, str, int, str, int, str, int, str] = Field( + serialization_alias="_f_tuple10", validation_alias="_f_tuple10" + ) + f_tuple11: tuple[int, str, int, str, int, str, int, str, int, str, int] = Field( + serialization_alias="_f_tuple11", validation_alias="_f_tuple11" + ) + f_tuple12: tuple[int, str, int, str, int, str, int, str, int, str, int, str] = ( + Field(serialization_alias="_f_tuple12", validation_alias="_f_tuple12") ) - f_tuple11: std.tuple.Tuple11[ - int, str, int, str, int, str, int, str, int, str, int - ] = Field(serialization_alias="_f_tuple11", validation_alias="_f_tuple11") - f_tuple12: std.tuple.Tuple12[ - int, str, int, str, int, str, int, str, int, str, int, str - ] = Field(serialization_alias="_f_tuple12", validation_alias="_f_tuple12") f_array: bytes = Field(serialization_alias="_f_array", validation_alias="_f_array") f_pointer_box: int = Field( serialization_alias="_f_pointer_box", validation_alias="_f_pointer_box" @@ -111,9 +111,6 @@ class ReflectapiDemoTestsBasicTestStructWithAllPrimitiveTypeFields(BaseModel): f_pointer_weak: int = Field( serialization_alias="_f_pointer_weak", validation_alias="_f_pointer_weak" ) - f_phantomdata: std.marker.PhantomData[int] = Field( - serialization_alias="_f_phantomdata", validation_alias="_f_phantomdata" - ) f_infallible: ReflectapiInfallible = Field( serialization_alias="_f_infallible", validation_alias="_f_infallible" ) @@ -229,10 +226,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithAllPrimitiveTypeFields, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap index e471fda5..4e377f24 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithArc, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap index 1008c8cf..c8ef15ac 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap @@ -138,10 +138,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithArcPointerOnly, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap index cd7ea6f0..71e18603 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithAttributesInputOnly, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap index 684f4b12..3235f52d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap @@ -140,10 +140,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithAttributesOutputOnly, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap index a91ea436..42f696d2 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap @@ -144,10 +144,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithExternalGenericTypeFallback, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap index e7e55b22..87c6433f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithFixedSizeArray, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap index 1af31387..1bf20103 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithHashMap, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap index 1ab1ce37..e83f9ae6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap @@ -138,10 +138,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithHashSetField, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap index a5c37011..d5ed898e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap @@ -156,10 +156,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithHashSetFieldGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap index 0a10db6d..ffcace0d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap @@ -143,11 +143,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructNested, ReflectapiDemoTestsBasicTestStructWithNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap index 47e42fe6..8521f6c1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap @@ -149,11 +149,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithNestedExternal, ReflectapiDemoTestsTestLibTestStructNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap index 556018c4..443e2fd6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithSelfViaArc, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap index 110f2ef3..922ae8b7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap @@ -132,10 +132,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithSkipField, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap index 3f0f4352..1edea56c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap @@ -154,11 +154,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicInputTestStructWithSkipFieldInput, ReflectapiDemoTestsBasicOutputTestStructWithSkipFieldInput, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap index 10931122..41b03f44 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap @@ -154,11 +154,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicInputTestStructWithSkipFieldOutput, ReflectapiDemoTestsBasicOutputTestStructWithSkipFieldOutput, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap index 86039e98..c78ec3d6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithTransformArray, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap index 79f30cfd..0a46f70d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithTransformBoth, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap index 9cc118e1..6688bd7b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap @@ -140,10 +140,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithTransformFallback, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap index 91733339..a506cd41 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap @@ -140,10 +140,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithTransformFallbackNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap index 3cedea13..68082964 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap @@ -156,11 +156,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicInputTestStructWithTransformInput, ReflectapiDemoTestsBasicOutputTestStructWithTransformInput, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap index 0215cb6f..a8774c94 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap @@ -156,11 +156,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicInputTestStructWithTransformOutput, ReflectapiDemoTestsBasicOutputTestStructWithTransformOutput, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap index b4faf404..c2bfe1b5 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap @@ -27,9 +27,7 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTuple(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) - f: std.tuple.Tuple2[int, str] = Field( - serialization_alias="_f", validation_alias="_f" - ) + f: tuple[int, str] = Field(serialization_alias="_f", validation_alias="_f") # Namespace classes for dotted access to types @@ -136,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithTuple, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap index 7aebb041..8f9e250e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap @@ -27,8 +27,8 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTuple12(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) - f: std.tuple.Tuple12[int, str, int, str, int, str, int, str, int, str, int, str] = ( - Field(serialization_alias="_f", validation_alias="_f") + f: tuple[int, str, int, str, int, str, int, str, int, str, int, str] = Field( + serialization_alias="_f", validation_alias="_f" ) @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithTuple12, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap index d571d26f..79f675c8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithVec, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap index 227cd7de..ae250e94 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap @@ -149,11 +149,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithVecExternal, ReflectapiDemoTestsTestLibTestStructNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap index e2477421..63f77171 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap @@ -147,11 +147,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithVecNested, ReflectapiDemoTestsTestLibTestStructNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap index 90a305a2..84bf6244 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap @@ -135,10 +135,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsBasicTestStructWithVecTwo, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum-4.snap index 6173bf83..50ca496f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum-4.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnum, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_empty-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_empty-4.snap index fbcccfd1..36f7fed8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_empty-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_empty-4.snap @@ -133,10 +133,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumEmpty, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_rename_num-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_rename_num-5.snap index 191eaa54..d9bd5811 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_rename_num-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_rename_num-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsNums, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap index cef2907c..32e8f70c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap @@ -239,10 +239,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithBasicVariantAndFieldsAndNamedFields, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_input-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_input-4.snap index 2efab501..9db0d381 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_input-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_input-4.snap @@ -140,10 +140,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithDiscriminantIgnored, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_output-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_output-4.snap index 7f70cb91..38210317 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_output-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_ignored_output-4.snap @@ -126,10 +126,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithDiscriminantIgnored, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_input-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_input-4.snap index ab1c93b7..9b66d29c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_input-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_input-4.snap @@ -142,10 +142,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithDiscriminant, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_output-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_output-4.snap index b171fd25..b90a6a75 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_output-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_discriminant_output-4.snap @@ -132,10 +132,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithDiscriminant, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap index 61731904..e1d592da 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap @@ -206,10 +206,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithEmptyVariantAndFields, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap index 167750d6..74faf32d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap @@ -223,10 +223,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithFields, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap index 9a4dea6c..18f37900 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap @@ -239,10 +239,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithGenerics, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap index 7210df08..0945e562 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap @@ -247,10 +247,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFields, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap index 9acb69c1..c74673dc 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap @@ -278,10 +278,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFieldsAndNamedFields, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap index b8c51e1b..75856ad6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap @@ -147,11 +147,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsA, ReflectapiDemoTestsEnumsE, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap index 34a2dbb7..8e8d3fb8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap @@ -151,10 +151,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsEnumsA, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap index 940a9111..6b976659 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap @@ -142,10 +142,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithCircularReference, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap index 75e11dbc..ad5b324e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap @@ -151,10 +151,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap index b1531387..e0cf496d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap @@ -49,9 +49,6 @@ class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericParent( f: reflectapi_demo.tests.generics.TestStructWithCircularReferenceGeneric[ reflectapi_demo.tests.generics.TestStructWithCircularReferenceGenericParent[T] ] = Field(serialization_alias="_f", validation_alias="_f") - f2: std.marker.PhantomData[T] = Field( - serialization_alias="_f2", validation_alias="_f2" - ) # Namespace classes for dotted access to types @@ -171,11 +168,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGeneric, ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericParent, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap index f874adc1..67bc79c6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap @@ -163,10 +163,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBox, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap index e6941e8f..847968f0 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap @@ -178,11 +178,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBox, ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithou_8e2b5cb9, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap index 735cd69d..9e198108 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap @@ -165,11 +165,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBox, ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithou_be812837, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap index fed37c58..2c801f1d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap @@ -157,11 +157,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithNestedGenericStruct, ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap index 50f453b1..4793191a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap @@ -160,11 +160,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithNestedGenericStructTwice, ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap index 7e89bdc5..6bd51ce8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap @@ -146,10 +146,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap index 2c91bb97..f066aabd 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap @@ -146,10 +146,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithVecGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap index 9b2f087f..121e87af 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap @@ -159,11 +159,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric, ReflectapiDemoTestsGenericsTestStructWithVecGenericGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap index 6901b80c..4374f62b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap @@ -159,11 +159,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsGenericsTestStructWithVecGeneric, ReflectapiDemoTestsGenericsTestStructWithVecGenericGenericGeneric, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap index d50e53df..3876cc9e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap @@ -158,10 +158,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTest, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap index 4bb8feb5..15311734 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTreeNode, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap index 10a36664..79aa4157 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap @@ -13,7 +13,7 @@ from __future__ import annotations # Standard library imports -from datetime import datetime, date, timedelta +from datetime import datetime, date from typing import Annotated, Any, Generic, Optional, TypeVar, Union # Third-party imports @@ -21,6 +21,7 @@ from pydantic import BaseModel, ConfigDict, Field # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty from reflectapi_runtime import ReflectapiInfallible @@ -28,7 +29,7 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStruct(BaseModel): model_config = ConfigDict(extra="ignore", populate_by_name=True) - duration: timedelta + duration: ReflectapiDuration naive_time: str naive_date: date naive_datetime: datetime @@ -37,15 +38,6 @@ class ReflectapiDemoTestsSerdeTestStruct(BaseModel): date_time_local: datetime -class StdTimeDuration(BaseModel): - """Time duration type""" - - model_config = ConfigDict(extra="ignore", populate_by_name=True) - - secs: int - nanos: int - - # Namespace classes for dotted access to types class reflectapi_demo: """Namespace for reflectapi_demo types.""" @@ -59,15 +51,6 @@ class reflectapi_demo: TestStruct = ReflectapiDemoTestsSerdeTestStruct -class std: - """Namespace for std types.""" - - class time: - """Namespace for time types.""" - - Duration = StdTimeDuration - - class AsyncInoutClient: """Async client for inout operations.""" @@ -159,10 +142,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStruct, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_enum-5.snap index 634856d6..3c460415 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_enum-5.snap @@ -133,10 +133,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeNever, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap index 28be0ff3..14466478 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap @@ -190,10 +190,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEmptyVariantsAdjacentlyTagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap index 6574c90f..95573819 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap @@ -229,10 +229,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEmptyVariantsExternallyTagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap index 36b0d460..724f0283 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap @@ -165,10 +165,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEmptyVariantsInterallyTagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap index 15eaef34..695c1a79 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap @@ -159,10 +159,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEmptyVariantsUntagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap index d407a258..36ef8a65 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap @@ -164,10 +164,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeMixed, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename-5.snap index 2075f7ae..cb3da155 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeMyEnum, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all-5.snap index 58d1e122..d04aaf2a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all-5.snap @@ -133,10 +133,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumRenameAll, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap index 727e0d1a..2aff9f50 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap @@ -226,10 +226,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumRenameAllOnVariant, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap index f2d8bcdf..b0742651 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap @@ -197,10 +197,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumRenameVariantField, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap index 14cffe64..a9577006 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap @@ -159,10 +159,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumTag, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap index deb29f4d..8b7f9035 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap @@ -176,10 +176,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumTagContent, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap index 73822b6c..1d33bd1d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap @@ -178,10 +178,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumTagContentRenameAll, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap index d8994b40..5c621368 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap @@ -149,10 +149,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumUntagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap index 27473b36..ec941ef1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap @@ -190,10 +190,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumWithFieldSkip, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap index 8c112fca..3e2a6c03 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap @@ -242,10 +242,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeLargeEnum, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap index 7140c0df..d32c290b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap @@ -203,10 +203,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumWithRenameToInvalidChars, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap index 3c20d076..c11ff87a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap @@ -181,10 +181,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeAction, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap index 7aa40afd..d9e12c54 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap @@ -191,11 +191,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestEnumWithVariantOther, ReflectapiDemoTestsSerdeOutputTestEnumWithVariantOther, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip-5.snap index fb96c2ab..d96df5e8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip-5.snap @@ -133,10 +133,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumWithVariantSkip, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_deserialize-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_deserialize-5.snap index 09831895..13159dcc 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_deserialize-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_deserialize-5.snap @@ -157,11 +157,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestEnumWithVariantSkipDeserialize, ReflectapiDemoTestsSerdeOutputTestEnumWithVariantSkipDeserialize, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_serialize-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_serialize-5.snap index d459bffa..d5c1a1fe 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_serialize-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_skip_serialize-5.snap @@ -157,11 +157,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestEnumWithVariantSkipSerialize, ReflectapiDemoTestsSerdeOutputTestEnumWithVariantSkipSerialize, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap index cce333e1..d14dc6d5 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap @@ -197,10 +197,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestEnumWithVariantUntagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap index 49ac7406..ae81e32b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap @@ -137,10 +137,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTest, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap index 0c73e2e5..cc5bd94e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap @@ -137,10 +137,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeKeywords, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap index 50bd4e71..dd67048c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap @@ -142,10 +142,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeSpecialNames, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap index 981a87f7..4e7b6cba 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap @@ -182,11 +182,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeMessage, ReflectapiDemoTestsSerdePayload, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap index 765cf200..16922892 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap @@ -215,11 +215,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeItem, ReflectapiDemoTestsSerdeStatus, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap index 49379b9a..48e5b429 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap @@ -210,11 +210,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeDrawing, ReflectapiDemoTestsSerdeShape, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap index 21233888..71a8d740 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap @@ -166,13 +166,23 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeA, ReflectapiDemoTestsSerdeB, ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeAReflectapiDemoTestsSerdeB, ReflectapiDemoTestsSerdeTest, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap index dfd425bc..3b758161 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap @@ -198,11 +198,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeOffer, ReflectapiDemoTestsSerdeOfferKind, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap index c58e7621..4545d457 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap @@ -154,12 +154,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeDocument, ReflectapiDemoTestsSerdeMetadata, ReflectapiDemoTestsSerdeTimestamps, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap index 7f3a3036..575ea623 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap @@ -185,11 +185,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdePriority, ReflectapiDemoTestsSerdeTask, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap index be651295..62abddac 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap @@ -208,12 +208,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeAudit, ReflectapiDemoTestsSerdeContent, ReflectapiDemoTestsSerdePost, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap index fc484f79..bcb68b7e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap @@ -151,12 +151,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInner, ReflectapiDemoTestsSerdeMiddle, ReflectapiDemoTestsSerdeOuter, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap index 271afb52..03e4ccc0 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap @@ -151,11 +151,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeK, ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTupleTuple0, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap index 32de3aec..b76f1b2f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap @@ -156,11 +156,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeCell, ReflectapiDemoTestsSerdeValue, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap index 3aa76220..e7f1ccd7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap @@ -170,10 +170,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap index a57a7287..b92c06da 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap @@ -202,10 +202,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeWrapper, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap index 81b98d4e..78161cdb 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap @@ -162,12 +162,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestFlattenInner, ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap index 71d2333f..aeef9147 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap @@ -259,13 +259,23 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIdent, ReflectapiDemoTestsSerdeTestFlattenIdentData, ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4, ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap index 0bd7a35e..d647b027 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap @@ -185,6 +185,7 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestLeafCollisionPair, @@ -193,7 +194,16 @@ for _model in [ ReflectapiDemoTestsSerdeModuleASample, ReflectapiDemoTestsSerdeModuleBSample, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap index 6aec7016..5f792e74 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap @@ -178,6 +178,7 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIdent, ReflectapiDemoTestsSerdeTestFlattenIdentData, @@ -185,7 +186,16 @@ for _model in [ ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4, ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap index 88f1e961..800b7d0c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap @@ -174,12 +174,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenInner, ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde356d19e7, ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap index c383ffc0..765c333b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap @@ -179,6 +179,7 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestFlattenInner, @@ -187,7 +188,16 @@ for _model in [ ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69, ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap index 3610e6b0..36f00c64 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap @@ -169,13 +169,23 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIdent, ReflectapiDemoTestsSerdeTestFlattenIdentData, ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4, ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap index 6d5c3270..6e6a6200 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap @@ -172,13 +172,23 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestFlattenInner, ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e, ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap index 41de37b1..b804d8d8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap @@ -177,13 +177,23 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestGenericWithConcreteFlattenReflectapiDemoTes_56981a18, ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestFlattenInner, ReflectapiDemoTestsSerdeTestGenericWithConcreteFlatten, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap index f554d7b9..61341683 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTest, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap index 7406954b..a0a7d86f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeUnderscored, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap index 164f6e45..a32fe6a4 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap @@ -143,10 +143,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeDeepNestedInnerDeepType, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap index 285b90f3..9eb0e7ba 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeSimpleTopLevel, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap index 97f444df..98cb026f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap @@ -137,10 +137,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTypeWithNumbers, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap index a20c4b42..56d53c09 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeComplex, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap index 1dbbcd72..99f4d54e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap @@ -200,12 +200,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTest, ReflectapiDemoTestsSerdeV1, ReflectapiDemoTestsSerdeV2, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap index 492243ba..0f98d5c9 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap @@ -156,11 +156,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTest, ReflectapiDemoTestsSerdeV1, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap index f6f87dfb..be574ac6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap @@ -186,10 +186,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestNewtypeVariantsAdjacentlyTagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap index 9854903a..6fdfc873 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap @@ -258,10 +258,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestNewtypeVariantsExternallyTagged, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap index 6c50f946..7df27341 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap @@ -170,12 +170,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeEnum, ReflectapiDemoTestsSerdeStrukt1, ReflectapiDemoTestsSerdeStrukt2, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap index 5377df6f..887dc718 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap @@ -135,10 +135,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap index cc1306e2..cd7ac8a7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap @@ -135,10 +135,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeCategory, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap index c3c94936..24ca2f8a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap @@ -141,11 +141,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructFrom, ReflectapiDemoTestsSerdeTestStructFromProxy, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap index 714c7f11..f559c5fe 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap @@ -141,11 +141,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructInto, ReflectapiDemoTestsSerdeTestStructIntoProxy, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap index 02829a89..56da25bd 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap @@ -132,10 +132,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeMyStruct, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap index 1630ab5f..4c51cde7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructRenameAll, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap index 2629c290..6cc73059 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap @@ -158,11 +158,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestStructRenameAllDifferently, ReflectapiDemoTestsSerdeOutputTestStructRenameAllDifferently, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap index 896f3898..a1451f40 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap @@ -142,10 +142,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructRenameAllPascalCase, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap index 197d6309..388698d7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap @@ -137,11 +137,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeMyStructInput, ReflectapiDemoTestsSerdeMyStructOutput, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap index 88394477..d2584daa 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap @@ -154,11 +154,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestStructRenameField, ReflectapiDemoTestsSerdeOutputTestStructRenameField, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap index 0da9797a..a66c446c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTest, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap index a87bb752..c72839e1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap @@ -141,11 +141,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructTryFormProxy, ReflectapiDemoTestsSerdeTestStructTryFrom, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap index 7c4cba08..08cbcd82 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap @@ -143,11 +143,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructWithFlatten, ReflectapiDemoTestsSerdeTestStructWithFlattenNested, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap index 8954c793..683e9e7b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap @@ -150,11 +150,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructWithFlattenNested, ReflectapiDemoTestsSerdeTestStructWithFlattenOptional, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap index 6b0d9feb..3f443e69 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap @@ -166,12 +166,22 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructRenameAll, ReflectapiDemoTestsSerdeTestStructWithFlattenNested, ReflectapiDemoTestsSerdeTestStructWithFlattenOptionalAndRequired, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap index 35b009e9..15e160c9 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap @@ -140,10 +140,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructWithRenameToInvalidChars, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap index f3dba1e8..f7949de7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap @@ -136,10 +136,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeStructName, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap index 5f99a87f..7d84b845 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap @@ -156,11 +156,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestStructWithSerdeDefault, ReflectapiDemoTestsSerdeOutputTestStructWithSerdeDefault, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap index c6b8a81f..858986f6 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap @@ -132,10 +132,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStructWithSerdeSkip, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap index a1111ef1..09f9642e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap @@ -158,11 +158,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestStructWithSerdeSkipDeserialize, ReflectapiDemoTestsSerdeOutputTestStructWithSerdeSkipDeserialize, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap index e052c654..9736940c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap @@ -158,11 +158,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestStructWithSerdeSkipSerialize, ReflectapiDemoTestsSerdeOutputTestStructWithSerdeSkipSerialize, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap index f6b478f2..e5156189 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap @@ -161,11 +161,21 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeInputTestStructWithSerdeSkipSerializeIf, ReflectapiDemoTestsSerdeOutputTestStructWithSerdeSkipSerializeIf, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap index 0657756c..3580a38a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap @@ -134,10 +134,20 @@ StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] # Rebuild models to resolve forward references +_rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestStruct, ]: + if not hasattr(_model, "model_rebuild"): + continue try: _model.model_rebuild() - except Exception: - pass + except Exception as _exc: + _rebuild_errors.append(f" - {_model.__name__}: {type(_exc).__name__}: {_exc}") +if _rebuild_errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(_rebuild_errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(_rebuild_errors) + ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap index 818dce42..87a593ab 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap @@ -10,6 +10,35 @@ expression: s "version": "1.0.0" }, "paths": { + "/codegen-regression": { + "description": "Regression-test endpoint pinning codegen bugs", + "post": { + "operationId": "codegen-regression", + "description": "Regression-test endpoint pinning codegen bugs", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.CodegenRegressionRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.CodegenRegressionResponse" + } + } + } + } + } + } + }, "/pets.cdc-events": { "description": "Stream of change data capture events for pets", "post": { @@ -385,10 +414,68 @@ expression: s }, "components": { "schemas": { + "bool": { + "description": "Boolean value", + "type": "boolean" + }, "f64": { "description": "64-bit floating point number", "type": "number" }, + "myapi.CodegenRegressionRequest": { + "type": "object", + "title": "myapi.CodegenRegressionRequest", + "required": [ + "order", + "policy", + "rate_limit" + ], + "properties": { + "order": { + "description": "Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability.", + "$ref": "#/components/schemas/myapi.order.OrderInsertData" + }, + "policy": { + "description": "Pulls in `order::Policy` for PhantomData (bug 4).", + "type": "object", + "title": "myapi.order.Policy", + "required": [ + "_context_marker", + "_output_marker", + "name" + ], + "properties": { + "_context_marker": { + "description": "Zero-sized phantom data", + "type": "null" + }, + "_output_marker": { + "description": "Zero-sized phantom data", + "type": "null" + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "rate_limit": { + "description": "Pulls in `order::RateLimit` for Duration (bug 3).", + "$ref": "#/components/schemas/myapi.order.RateLimit" + } + } + }, + "myapi.CodegenRegressionResponse": { + "type": "object", + "title": "myapi.CodegenRegressionResponse", + "required": [ + "ok" + ], + "properties": { + "ok": { + "$ref": "#/components/schemas/bool" + } + } + }, "myapi.model.Behavior": { "oneOf": [ { @@ -581,6 +668,66 @@ expression: s } } }, + "myapi.order.OrderInsertData": { + "description": "Bug 1 (namespace alias mismatch). The struct name starts with\nthe parent namespace\\'s cap (`Order…`) — the alias-stripping\npass used to strip the leading cap from the namespace alias\n(`order.InsertData = OrderInsertData`) while field annotations\nkept the un-stripped form, producing `order.OrderInsertData`\nwhich doesn\\'t resolve at `model_rebuild()` time.", + "type": "object", + "title": "myapi.order.OrderInsertData", + "required": [ + "alternative_part_number", + "identity" + ], + "properties": { + "alternative_part_number": { + "description": "Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "description": "Tuple holding 2 elements", + "type": "array", + "prefixItems": [ + { + "$ref": "#/components/schemas/std.string.String" + }, + { + "$ref": "#/components/schemas/std.string.String" + } + ] + } + ] + }, + "identity": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.order.RateLimit": { + "description": "Bug 3 (Duration shape). serde emits `Duration` as\n`{\\\"secs\\\": , \\\"nanos\\\": }`. Pydantic\\'s `timedelta`\nvalidator rejects that shape, so any response with a Duration\nfailed validation.", + "type": "object", + "title": "myapi.order.RateLimit", + "required": [ + "max_wait", + "retry_after" + ], + "properties": { + "max_wait": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/std.time.Duration" + } + ] + }, + "retry_after": { + "$ref": "#/components/schemas/std.time.Duration" + } + } + }, "myapi.proto.InternalError": { "type": "object", "title": "myapi.proto.InternalError", @@ -846,10 +993,35 @@ expression: s "description": "UTF-8 encoded string", "type": "string" }, + "std.time.Duration": { + "description": "Time duration type", + "type": "object", + "title": "std.time.Duration", + "required": [ + "nanos", + "secs" + ], + "properties": { + "nanos": { + "$ref": "#/components/schemas/u32" + }, + "secs": { + "$ref": "#/components/schemas/u64" + } + } + }, "std.tuple.Tuple0": { "description": "Unit type", "type": "null" }, + "u32": { + "description": "32-bit unsigned integer", + "type": "integer" + }, + "u64": { + "description": "64-bit unsigned integer", + "type": "integer" + }, "u8": { "description": "8-bit unsigned integer", "type": "integer" diff --git a/reflectapi-python-runtime/.coverage b/reflectapi-python-runtime/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..8ae2889e30489acce4b9acbc14cafbc14daf49a7 GIT binary patch literal 53248 zcmeI4eQ*@z9mk)&-Mh=Z?Ou7w5hBgP3{3)g@eC=Y3?bpdG@)x-P>+msu;4^p3%~hAvTdPVHnaPii8j!yhHG| zH!tjP_5p-U$^KZoKGJaCgphDM33^r$VSQ+_P#^3Fo(((_{F-k?K=Ic38sG$ONB{{S z0VHtE5Xj#e^hPQwn1RiP6i>;9CM9Ie`V4*ZI|~;qUnnkLaO2X2q7^6B_(j<2>%|44 zmgy0*vL?1GDOpt1q>_*fMePucPT5YK)8!x=( zx<+}{WhaYHb_AxoffsH_00|%g zB!C2v01`j~NB{{S0VIF~kifM^fMr~am#+U^!ciieh7a7301`j~NB{{S0VIF~kN^@u z0!RP}Ac4;%fuM_>BG`WmxjxLWl@*2m0L*TPHZ?T+p;j*8BoR&uAABw=h(;p;B!C2v z01`j~NB{{S0VIF~kN^@u0wV;1>=efS8o=deD}9A;0qF1lVSf`5b_(C-&+rfOvqEo# zwuHpsZ-QOHP+(`^CjTk_R!D#w5kN&*;pk^;%A)cjPweTB77^%#Jo>oj&0M?)hCOfc88mwRHpT z`Au8ut9Vdvc2r%~k}}x6Cge1etfU!=lmhL}AZV{}((2B>YzA%;))hSlc;Jrc4n@|X zq_qJkX|9tcC37GTH-3Zo?S2rCIw`K}6#Y6M=sM~$fJD`^8O;Fo)-q6^@1)gfC7Ddg zJ@Cv8kYDWu`Poj&CsK;6Qp;C)K-y7fH~O-co47&QQ8&?K;H{d?*~&Q$T<%TCS@QuQ zlnU!KmAGUiIzf4*3zVCjY)W{t3>99>IE_0NQ&h!>#Xy|?{vYzcKgjdj z0*{5J1{U$n{?oy3{}27Mg8M^neo-eotTz%s0!RP}AOR$R1dsqBYq|U!#>ora;`%?_ z%H`)ds$IJN7glrmW@pt4>wn&HCdKuCNa6A;9JP9^^?z_3m!IosNhRz5z)CJ3byVD1 z|N9+xnbP&YZ#|cv@2J(I*8gQ|xcqEKcaxI!zuR%(qt^eN<5cYR zKf8*{H#yprh4sG+#+JE0C&$~8^*=uU4`*~FfCP{L5K3A}Ja0!RP}AOR$R1dsp{Kmter2_OL^fCR2V0xTQynfL#PenfHywn~zKY6;BCJPRn ze$+LzWnw^d<=co$6ebM0_mJ#3Ld3%@AlXvxp=EPV-f-{XbI(qBGg|iRX7blp;;i6? zcrgHFx6HgyJM^9_Kaq1YzJVFu;U4cRKPGKwfBNj%!NL4wGHDrmQtl0Ie=B<3{`es- zKwh4E)dw&c$LMjzOBh9!r2b>G3*@86!=@lJO) z+uYJbJY*<)Rgtz!|A@ISy8hI1^t3H;aQ%l&zv_)W^WI9=WEKqlD%(}J{NQ?b`RpT= zyV?eK&$wgpKz)K)K#wjk)fuw^LGF5+-)b` zy`?oW;3oYi<~;Yti4S)DyYIp;e$E?*Crw>N4!-!nxY`E^JC5OezM31!Q8r)o#Ko5` z!N!>P{|noQ@R9Joa7s8N>=m{N$AtsJPlTt1Bf{&#eqm79CF}sqxFG=~fCP{L57k={&x^4N+AIxfCP{L5 + + + + + /home/brian/dev/reflectapi/reflectapi-python-runtime/src/reflectapi_runtime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1-py3-none-any.whl b/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..b200faf5c9c845b4f8cc110d92885fd1fd55e98a GIT binary patch literal 34048 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 YP7?In8wUV@|9+l)YdQn^{nxwy4*<&dh5!Hn literal 0 HcmV?d00001 diff --git a/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1.tar.gz b/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..6750de006a319abbd3be32609fc952a6fc8512c8 GIT binary patch literal 60036 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 literal 0 HcmV?d00001 diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py b/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py index a0e82948..0b184290 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/__init__.py @@ -20,6 +20,7 @@ ) from .batch import BatchClient from .client import AsyncClientBase, ClientBase +from .duration import ReflectapiDuration from .transport import AsyncClient, Client, Request, Response from .exceptions import ( ApiError, @@ -86,6 +87,7 @@ "AsyncMiddleware", "MockClient", "NetworkError", + "ReflectapiDuration", "ReflectapiEmpty", "ReflectapiInfallible", "ReflectapiPartialModel", diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/duration.py b/reflectapi-python-runtime/src/reflectapi_runtime/duration.py new file mode 100644 index 00000000..94b7c2e7 --- /dev/null +++ b/reflectapi-python-runtime/src/reflectapi_runtime/duration.py @@ -0,0 +1,73 @@ +"""Wire-format adapter for Rust's ``std::time::Duration``. + +serde serialises ``Duration`` as ``{"secs": , "nanos": }``. +Pydantic v2's built-in ``timedelta`` validator accepts ISO-8601 strings, +ints, and floats — it does **not** accept the ``{secs, nanos}`` dict +that the server actually sends. The bare ``timedelta`` annotation +emitted by older codegen therefore failed validation on every response +that carried a ``Duration`` field. + +``ReflectapiDuration`` is the type the generated client uses instead. It +preserves the ergonomic Python ``timedelta`` API (``td.total_seconds()``, +arithmetic, comparisons) and round-trips the serde shape: + +- **Validating** (server → client): a ``{"secs": …, "nanos": …}`` dict + is converted to a ``timedelta``; ints/floats and existing + ``timedelta`` instances pass through unchanged so users can construct + models from Python values directly. +- **Serialising** (client → server): the ``timedelta`` is written back + as ``{"secs": , "nanos": }`` so the wire payload round-trips + cleanly through ``serde::Deserialize`` on the server. + +Precision caveat: ``timedelta`` stores microseconds, so the bottom three +decimal digits of ``nanos`` are truncated on round-trip. If a server +ever sends a ``nanos`` value not divisible by 1 000 (sub-microsecond), +the recovered ``nanos`` will be the closest microsecond-aligned value. +For the durations reflectapi APIs typically carry (timeouts, retry +hints, rate-limit windows) this is well below the noise floor. +""" + +from __future__ import annotations + +from datetime import timedelta +from typing import Annotated, Any + +from pydantic import BeforeValidator, PlainSerializer + +_NS_PER_SECOND = 1_000_000_000 + + +def _validate(value: Any) -> Any: + """Accept serde's ``{secs, nanos}`` dict; pass other forms through.""" + if isinstance(value, dict): + secs = value.get("secs", 0) + nanos = value.get("nanos", 0) + if not isinstance(secs, (int, float)) or not isinstance(nanos, (int, float)): + return value # let Pydantic raise its own validation error + return timedelta(seconds=secs, microseconds=nanos / 1_000) + return value + + +def _serialise(value: timedelta) -> dict[str, int]: + """Emit the wire shape Rust's ``serde::Deserialize`` expects.""" + if not isinstance(value, timedelta): + # Pydantic already coerced; if anything else slips through, surface + # the failure rather than papering over it. + raise TypeError( + f"ReflectapiDuration serialiser expected a timedelta, got {type(value).__name__}" + ) + # Pull total nanoseconds out of the timedelta in two integer pieces + # so we never round-trip through float and lose precision below the + # microsecond level. + total_us = (value.days * 86_400 + value.seconds) * 1_000_000 + value.microseconds + secs, micros = divmod(total_us, 1_000_000) + nanos = micros * 1_000 + return {"secs": secs, "nanos": nanos} + + +ReflectapiDuration = Annotated[ + timedelta, + BeforeValidator(_validate), + PlainSerializer(_serialise, when_used="json"), +] +"""Pydantic field type for ``std::time::Duration`` — see module docstring.""" diff --git a/reflectapi-python-runtime/tests/test_codegen_regressions.py b/reflectapi-python-runtime/tests/test_codegen_regressions.py new file mode 100644 index 00000000..86fd7f8c --- /dev/null +++ b/reflectapi-python-runtime/tests/test_codegen_regressions.py @@ -0,0 +1,90 @@ +"""Regression tests for runtime types that fix codegen bugs. + +These tests pin the runtime contracts behind four user-reported bugs in +the Python codegen. They live in the runtime test suite — alongside +``ReflectapiPartialModel`` and the rest of the runtime API — so they +run on every push without needing a full end-to-end demo-client +regeneration. + +Each test is named after the bug it pins and includes a brief comment +linking it to the failure mode it locks down. The complementary end-to- +end check (regenerate the demo client + import it strictly) lives in +``.github/workflows/ci.yaml::python-codegen-smoke``. +""" + +from __future__ import annotations + +import json +from datetime import timedelta + +import pytest +from pydantic import BaseModel, ValidationError + +from reflectapi_runtime import ReflectapiDuration + + +class TestDurationRoundTrip: + """Bug 3: serde emits ``Duration`` as ``{"secs": …, "nanos": …}``; + Pydantic's bare ``timedelta`` validator rejects that shape. The + ``ReflectapiDuration`` Annotated type round-trips both directions. + """ + + def _model(self): + class M(BaseModel): + d: ReflectapiDuration + + return M + + def test_validate_secs_nanos_dict(self): + M = self._model() + m = M.model_validate({"d": {"secs": 30, "nanos": 0}}) + assert m.d == timedelta(seconds=30) + + def test_validate_with_nanos(self): + M = self._model() + m = M.model_validate({"d": {"secs": 1, "nanos": 500_000_000}}) + # timedelta resolves to microsecond precision; we get 1.5 seconds. + assert m.d == timedelta(seconds=1, microseconds=500_000) + + def test_validate_existing_timedelta(self): + """Code that already has a ``timedelta`` should pass through unchanged.""" + M = self._model() + m = M.model_validate({"d": timedelta(seconds=5)}) + assert m.d == timedelta(seconds=5) + + def test_serialise_emits_secs_nanos(self): + M = self._model() + m = M(d=timedelta(seconds=30, microseconds=500_000)) + payload = json.loads(m.model_dump_json()) + assert payload == {"d": {"secs": 30, "nanos": 500_000_000}} + + def test_serialise_subsecond(self): + M = self._model() + m = M(d=timedelta(microseconds=1_500)) # 1.5 ms + payload = json.loads(m.model_dump_json()) + # 1500 microseconds == 1_500_000 nanoseconds (well under one second). + assert payload == {"d": {"secs": 0, "nanos": 1_500_000}} + + def test_round_trip(self): + M = self._model() + original = M(d=timedelta(hours=2, seconds=3, microseconds=4)) + reloaded = M.model_validate_json(original.model_dump_json()) + assert reloaded.d == original.d + + def test_round_trip_negative_duration_rejected(self): + """serde's `Duration` is unsigned; we don't pretend to support negatives.""" + M = self._model() + m = M(d=timedelta(seconds=-1)) + # We *do* serialise it (Python's timedelta is signed), but the + # resulting nanos field will be negative. The test pins the + # current behaviour — negative durations land on the wire as a + # negative `secs` integer, which a Rust server would reject. + payload = json.loads(m.model_dump_json()) + # divmod(-1_000_000, 1_000_000) == (-1, 0) → secs=-1, nanos=0 + assert payload == {"d": {"secs": -1, "nanos": 0}} + + def test_invalid_dict_shape_falls_through_to_pydantic_error(self): + """A dict without the right keys should produce a clean ValidationError.""" + M = self._model() + with pytest.raises(ValidationError): + M.model_validate({"d": {"secs": "not-a-number"}}) diff --git a/reflectapi-schema/src/lib.rs b/reflectapi-schema/src/lib.rs index 9961da71..d1d60f17 100644 --- a/reflectapi-schema/src/lib.rs +++ b/reflectapi-schema/src/lib.rs @@ -1023,6 +1023,17 @@ impl Fields { Fields::None => [].iter_mut(), } } + + /// Keep only fields for which `predicate` returns `true`. + pub fn retain(&mut self, mut predicate: F) + where + F: FnMut(&Field) -> bool, + { + match self { + Fields::Named(fields) | Fields::Unnamed(fields) => fields.retain(|f| predicate(f)), + Fields::None => {} + } + } } impl Index for Fields { diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index 9ebe70c7..ed32457a 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -225,16 +225,54 @@ fn python_type_mappings() -> &'static HashMap<&'static str, PythonTypeMapping> { m.insert( "std::time::Duration", PythonTypeMapping { - type_hint: "timedelta", - imports: &["from datetime import timedelta"], - runtime_imports: &[], - provided_by_runtime: false, + // serde renders Duration as `{"secs": , "nanos": }`. + // Pydantic's bare `timedelta` validator rejects that + // dict shape (it expects ISO-8601 / int / float). + // `ReflectapiDuration` is `Annotated[timedelta, + // BeforeValidator(...), PlainSerializer(...)]` — + // accepts both the serde wire shape and python + // timedelta values, emits the serde shape on the way + // out. + type_hint: "ReflectapiDuration", + imports: &[], + runtime_imports: &["ReflectapiDuration"], + provided_by_runtime: true, ignore_type_arguments: false, }, ); // Special types m.insert("std::tuple::Tuple0", PythonTypeMapping::simple("None")); + // Rust tuples of arity >=1 serialise as JSON arrays of the same + // arity. Pydantic's `tuple[A, B, …]` annotation validates that + // exactly: a fixed-length heterogeneous sequence. Anything not + // mapped here would otherwise render via + // `type_name_to_python_ref` as `std.tuple.TupleN[A, B]`, which + // is a dangling reference (there's no `std.tuple` submodule). + // Cover up to Tuple16 — matches the arity ceiling enforced by + // `reflectapi-schema` and Rust's own std trait impls. + for arity in 1..=16 { + let name: &'static str = match arity { + 1 => "std::tuple::Tuple1", + 2 => "std::tuple::Tuple2", + 3 => "std::tuple::Tuple3", + 4 => "std::tuple::Tuple4", + 5 => "std::tuple::Tuple5", + 6 => "std::tuple::Tuple6", + 7 => "std::tuple::Tuple7", + 8 => "std::tuple::Tuple8", + 9 => "std::tuple::Tuple9", + 10 => "std::tuple::Tuple10", + 11 => "std::tuple::Tuple11", + 12 => "std::tuple::Tuple12", + 13 => "std::tuple::Tuple13", + 14 => "std::tuple::Tuple14", + 15 => "std::tuple::Tuple15", + 16 => "std::tuple::Tuple16", + _ => unreachable!(), + }; + m.insert(name, PythonTypeMapping::simple("tuple")); + } m.insert("serde_json::Value", PythonTypeMapping::simple("Any")); // Wrapper types (transparent) @@ -1007,6 +1045,50 @@ fn render_struct_with_flatten_standard( } /// Validate that all type references in the schema exist +/// Drop every `PhantomData` field from every struct/variant in +/// both input and output typespaces. PhantomData is purely a Rust +/// type-system marker — serde renders it as `null` and the field +/// carries no information for a wire consumer. Leaving it in the +/// schema forced the Python codegen to render +/// `std.marker.PhantomData[T]` annotations that didn't resolve. +fn strip_phantom_data_fields(schema: &mut Schema) { + fn strip_from_typespace( + typespace_name_iter: impl IntoIterator, + ts: &mut reflectapi_schema::Typespace, + ) { + for name in typespace_name_iter { + let Some(ty) = ts.get_type_mut(&name) else { + continue; + }; + match ty { + reflectapi_schema::Type::Struct(s) => { + s.fields + .retain(|f| f.type_ref.name != "std::marker::PhantomData"); + } + reflectapi_schema::Type::Enum(e) => { + for v in &mut e.variants { + v.fields + .retain(|f| f.type_ref.name != "std::marker::PhantomData"); + } + } + _ => {} + } + } + } + let input_names: Vec = schema + .input_types + .types() + .map(|t| t.name().to_string()) + .collect(); + strip_from_typespace(input_names, &mut schema.input_types); + let output_names: Vec = schema + .output_types + .types() + .map(|t| t.name().to_string()) + .collect(); + strip_from_typespace(output_names, &mut schema.output_types); +} + /// Walk every input/output struct in the schema and return `true` if /// any field uses the three-state `reflectapi::Option`. Used to /// decide whether to import `ReflectapiPartialModel` into the generated @@ -1459,6 +1541,7 @@ fn modules_from_rendered_types( if let Some(rendered_type) = rendered_types.remove(&original_type_name) { module.types.push(templates::ModuleType { rendered: rendered_type, + rust_path: original_type_name.clone(), }); } } @@ -1470,6 +1553,15 @@ fn build_python_generation( mut schema: Schema, config: &Config, ) -> anyhow::Result { + // PhantomData carries no wire data in Rust. serde serialises + // each occurrence as `null`, but the field still appears in the + // reflectapi schema with `type_ref.name == "std::marker::PhantomData"`. + // Render-time the codegen used to emit + // `std.marker.PhantomData[T]`, a dangling reference that broke + // `model_rebuild()`. Strip the fields entirely — they have no + // observable behaviour for a Python client. + strip_phantom_data_fields(&mut schema); + // Consolidate input/output types FIRST so both the SemanticSchema and // the raw Schema share the same unified type names. let all_type_names = schema.consolidate_types(); @@ -1856,8 +1948,26 @@ fn render_external_types_and_rebuilds(rebuild_models: &[String]) -> String { ]; if !rebuild_models.is_empty() { + // Same strict-rebuild contract as `render_rebuild_file` (see + // its comment for the rationale). external_types_and_rebuilds.push(format!( - "for _model in [\n {},\n]:\n try:\n _model.model_rebuild()\n except Exception:\n pass", + "_rebuild_errors: list[str] = []\n\ + for _model in [\n {},\n]:\n\ + \x20 if not hasattr(_model, \"model_rebuild\"):\n\ + \x20 continue\n\ + \x20 try:\n\ + \x20 _model.model_rebuild()\n\ + \x20 except Exception as _exc:\n\ + \x20 _rebuild_errors.append(\ + f\" - {{_model.__name__}}: {{type(_exc).__name__}}: {{_exc}}\")\n\ + if _rebuild_errors:\n\ + \x20 raise RuntimeError(\n\ + \x20 \"reflectapi: failed to rebuild \" + str(len(_rebuild_errors)) + \ + \" generated model(s). This usually means the codegen emitted an annotation \ + pointing at a symbol that was never defined (a dangling type reference). \ + Fix the codegen rather than catching this error.\\n\" + \ + \"\\n\".join(_rebuild_errors)\n\ + \x20 )", rebuild_models.join(",\n ") )); } @@ -1956,14 +2066,45 @@ fn module_aliases(module: &templates::Module, ns_path: &[String]) -> BTreeMap.`, so the namespace must also + // answer to that name or `model_rebuild()` raises + // AttributeError. Only emit when the leaf differs from the + // stripped form (otherwise we're duplicating an alias). + if !rust_leaf.is_empty() { + let leaf_pascal = improve_class_name_part(rust_leaf); + if let Some(flat_name) = primary_flat_name { + if !leaf_pascal.is_empty() && !aliases.contains_key(&leaf_pascal) { + aliases.insert(leaf_pascal, flat_name); + } } } } @@ -2094,10 +2235,17 @@ fn render_module_file( if !body.trim().is_empty() { body.push('\n'); } + // Narrow the suppression to `ImportError` only: the wide + // `except Exception: pass` here used to swallow every + // `model_rebuild()` failure raised by `_rebuild_models()`, + // which is the same class of bug we want to surface. The + // remaining ImportError suppression covers the legitimate case + // where this namespace is imported standalone before the root + // package finishes initialising. body.push_str("try:\n"); body.push_str(" from .._rebuild import rebuild_models as _rebuild_models\n\n"); body.push_str(" _rebuild_models()\n"); - body.push_str("except Exception:\n"); + body.push_str("except ImportError:\n"); body.push_str(" pass\n"); } @@ -2209,15 +2357,42 @@ fn render_rebuild_file( body.push_str(&format!(" _types.{root} = {root}\n")); } } + // Run `model_rebuild()` against every generated model and + // collect every failure into one structured report. The old + // shape (`try: model_rebuild() except: pass`) silently + // swallowed dangling-annotation bugs at import time — they + // surfaced much later as `AttributeError: module has no + // attribute X` when a user tried to construct a model. By + // raising here we catch the whole class of "codegen emitted a + // dangling reference" bugs the moment the package loads, + // making CI smoke tests like `python -c 'import api_client'` + // genuinely informative. + body.push_str(" errors: list[str] = []\n"); body.push_str(" for _model in [\n"); for model in &generation.rebuild_models { body.push_str(&format!(" {model},\n")); } body.push_str(" ]:\n"); + // Some rendered types are Python enums (or other non-Pydantic + // classes) — they don't have `model_rebuild()` and don't need + // one. Skip them without flagging an error. + body.push_str(" if not hasattr(_model, \"model_rebuild\"):\n"); + body.push_str(" continue\n"); body.push_str(" try:\n"); body.push_str(" _model.model_rebuild()\n"); - body.push_str(" except Exception:\n"); - body.push_str(" pass\n"); + body.push_str(" except Exception as exc:\n"); + body.push_str( + " errors.append(f\" - {_model.__name__}: {type(exc).__name__}: {exc}\")\n", + ); + body.push_str(" if errors:\n"); + body.push_str( + " raise RuntimeError(\n \ + \"reflectapi: failed to rebuild \" + str(len(errors)) + \ + \" generated model(s). This usually means the codegen \ + emitted an annotation pointing at a symbol that was never \ + defined (a dangling type reference). Fix the codegen \ + rather than catching this error.\\n\" + \"\\n\".join(errors)\n )\n", + ); } lines.push(body); @@ -4884,6 +5059,13 @@ pub mod templates { pub struct ModuleType { /// The rendered Python source code for this type. pub rendered: String, + /// Original fully-qualified Rust type name (e.g. + /// `myapi::order::OrderInsertData`). Used by the alias + /// generator to expose the type under both its + /// flat-prefix-stripped form (`InsertData`) and its Rust-leaf + /// form (`OrderInsertData`); field annotations elsewhere use + /// the Rust leaf, so both names must be live in the namespace. + pub rust_path: String, } pub struct Module { From 82fc5928751fb42dfe5803a98b7d6a93393f03c7 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 11:40:27 +1200 Subject: [PATCH 03/17] chore: gitignore coverage + dist artefacts (drop from prior commit) --- .gitignore | 5 + reflectapi-python-runtime/.coverage | Bin 53248 -> 0 bytes reflectapi-python-runtime/coverage.xml | 1595 ----------------- ...flectapi_runtime-0.17.2a1-py3-none-any.whl | Bin 34048 -> 0 bytes .../dist/reflectapi_runtime-0.17.2a1.tar.gz | Bin 60036 -> 0 bytes 5 files changed, 5 insertions(+), 1595 deletions(-) delete mode 100644 reflectapi-python-runtime/.coverage delete mode 100644 reflectapi-python-runtime/coverage.xml delete mode 100644 reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1-py3-none-any.whl delete mode 100644 reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1.tar.gz diff --git a/.gitignore b/.gitignore index b83ae0e9..f17784cb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ docs/book .vscode/settings.json *.pyc __pycache__/ + +# Python coverage artefacts +reflectapi-python-runtime/.coverage +reflectapi-python-runtime/coverage.xml +reflectapi-python-runtime/dist/ diff --git a/reflectapi-python-runtime/.coverage b/reflectapi-python-runtime/.coverage deleted file mode 100644 index 8ae2889e30489acce4b9acbc14cafbc14daf49a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53248 zcmeI4eQ*@z9mk)&-Mh=Z?Ou7w5hBgP3{3)g@eC=Y3?bpdG@)x-P>+msu;4^p3%~hAvTdPVHnaPii8j!yhHG| zH!tjP_5p-U$^KZoKGJaCgphDM33^r$VSQ+_P#^3Fo(((_{F-k?K=Ic38sG$ONB{{S z0VHtE5Xj#e^hPQwn1RiP6i>;9CM9Ie`V4*ZI|~;qUnnkLaO2X2q7^6B_(j<2>%|44 zmgy0*vL?1GDOpt1q>_*fMePucPT5YK)8!x=( zx<+}{WhaYHb_AxoffsH_00|%g zB!C2v01`j~NB{{S0VIF~kifM^fMr~am#+U^!ciieh7a7301`j~NB{{S0VIF~kN^@u z0!RP}Ac4;%fuM_>BG`WmxjxLWl@*2m0L*TPHZ?T+p;j*8BoR&uAABw=h(;p;B!C2v z01`j~NB{{S0VIF~kN^@u0wV;1>=efS8o=deD}9A;0qF1lVSf`5b_(C-&+rfOvqEo# zwuHpsZ-QOHP+(`^CjTk_R!D#w5kN&*;pk^;%A)cjPweTB77^%#Jo>oj&0M?)hCOfc88mwRHpT z`Au8ut9Vdvc2r%~k}}x6Cge1etfU!=lmhL}AZV{}((2B>YzA%;))hSlc;Jrc4n@|X zq_qJkX|9tcC37GTH-3Zo?S2rCIw`K}6#Y6M=sM~$fJD`^8O;Fo)-q6^@1)gfC7Ddg zJ@Cv8kYDWu`Poj&CsK;6Qp;C)K-y7fH~O-co47&QQ8&?K;H{d?*~&Q$T<%TCS@QuQ zlnU!KmAGUiIzf4*3zVCjY)W{t3>99>IE_0NQ&h!>#Xy|?{vYzcKgjdj z0*{5J1{U$n{?oy3{}27Mg8M^neo-eotTz%s0!RP}AOR$R1dsqBYq|U!#>ora;`%?_ z%H`)ds$IJN7glrmW@pt4>wn&HCdKuCNa6A;9JP9^^?z_3m!IosNhRz5z)CJ3byVD1 z|N9+xnbP&YZ#|cv@2J(I*8gQ|xcqEKcaxI!zuR%(qt^eN<5cYR zKf8*{H#yprh4sG+#+JE0C&$~8^*=uU4`*~FfCP{L5K3A}Ja0!RP}AOR$R1dsp{Kmter2_OL^fCR2V0xTQynfL#PenfHywn~zKY6;BCJPRn ze$+LzWnw^d<=co$6ebM0_mJ#3Ld3%@AlXvxp=EPV-f-{XbI(qBGg|iRX7blp;;i6? zcrgHFx6HgyJM^9_Kaq1YzJVFu;U4cRKPGKwfBNj%!NL4wGHDrmQtl0Ie=B<3{`es- zKwh4E)dw&c$LMjzOBh9!r2b>G3*@86!=@lJO) z+uYJbJY*<)Rgtz!|A@ISy8hI1^t3H;aQ%l&zv_)W^WI9=WEKqlD%(}J{NQ?b`RpT= zyV?eK&$wgpKz)K)K#wjk)fuw^LGF5+-)b` zy`?oW;3oYi<~;Yti4S)DyYIp;e$E?*Crw>N4!-!nxY`E^JC5OezM31!Q8r)o#Ko5` z!N!>P{|noQ@R9Joa7s8N>=m{N$AtsJPlTt1Bf{&#eqm79CF}sqxFG=~fCP{L57k={&x^4N+AIxfCP{L5 - - - - - /home/brian/dev/reflectapi/reflectapi-python-runtime/src/reflectapi_runtime - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1-py3-none-any.whl b/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1-py3-none-any.whl deleted file mode 100644 index b200faf5c9c845b4f8cc110d92885fd1fd55e98a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34048 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 YP7?In8wUV@|9+l)YdQn^{nxwy4*<&dh5!Hn diff --git a/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1.tar.gz b/reflectapi-python-runtime/dist/reflectapi_runtime-0.17.2a1.tar.gz deleted file mode 100644 index 6750de006a319abbd3be32609fc952a6fc8512c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60036 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 From 9e9aad9bcad1fc665537aef20e3da9772ee62f27 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 12:16:04 +1200 Subject: [PATCH 04/17] codegen(python): widen coverage and harden against fragile inputs Adds coverage fixtures to the demo crate that exercise Python codegen edge cases the smoke test would otherwise miss, plus the fixes those fixtures uncovered. Python codegen fixes - Field names exact-matching Pydantic v2 methods (`model_dump`, `model_validate`, `model_config`, etc.) are renamed with a trailing underscore so the methods aren't shadowed at the instance level. - `protected_namespaces=()` is set on every generated `ConfigDict`, letting any user field starting with `model_` exist without Pydantic raising at class construction. - `Vec` renders as `list[int]` instead of `bytes`. serde's default JSON shape for `Vec` is a JSON array of integers, not a base64 string; the old mapping made Pydantic's `bytes` validator reject every valid wire payload. OpenAPI codegen fix - Add an in-progress set keyed by full monomorphization so generic self-referential types no longer recurse forever and blow the stack. Non-generic recursion was already handled by the stub-on-insert into `components.schemas`. Coverage fixtures The demo's `coverage` module exercises: - Python keyword field names (`class`, `from`, `import`, `lambda`, `return`, `yield`, `None`, `True`). - Pydantic-reserved field names (`model_config`, `model_fields_set`, `model_dump_json`). - Self-referential and mutually recursive structs. - Generic recursive types instantiated with concrete arguments. - Enums with variants whose names are Python keywords. - HashMap with non-string keys. - `#[serde(transparent)]` newtypes. - Three-state Option wrapping a regular Option. - Field names colliding with imports brought in by the generated module (`BaseModel`, `Field`, `Annotated`, `Generic`, `TypeVar`). - Empty structs. - Docstrings containing quotes, apostrophes, backslashes, and triple quotes. - Type names that shadow Pydantic-imported symbols (`BaseModel`). - The same generic used with multiple concrete type arguments. - Fields with non-`None` serde defaults. The CI smoke test exercises every fixture by regenerating the demo client and importing it; the strict `_rebuild_models()` raises on any dangling type reference. Demo client snapshots, OpenAPI spec, Rust/TS generated clients and the affected Python demo tests are regenerated and updated for the new module names. --- .github/workflows/ci.yaml | 10 +- .../clients/python/api_client/_client.py | 76 +- .../clients/python/api_client/_rebuild.py | 51 +- .../clients/python/api_client/generated.py | 53 +- .../python/api_client/myapi/__init__.py | 46 +- .../api_client/myapi/adversarial/__init__.py | 345 ++++++++ .../api_client/myapi/coverage/__init__.py | 337 ++++++++ .../python/api_client/myapi/model/__init__.py | 20 +- .../api_client/myapi/model/input/__init__.py | 4 +- .../api_client/myapi/model/output/__init__.py | 4 +- .../python/api_client/myapi/order/__init__.py | 36 +- .../python/api_client/myapi/proto/__init__.py | 53 +- .../python/api_client/reflectapi/__init__.py | 12 +- .../clients/python/tests/conftest.py | 49 +- .../tests/generated_code/test_models.py | 60 +- .../generated_code/test_rust_type_mappings.py | 5 +- .../test_integration_edge_cases.py | 73 +- .../tests/integration/test_performance.py | 31 +- .../integration/test_tagged_enums_e2e.py | 5 +- .../clients/rust/generated/src/generated.rs | 209 ++++- .../clients/typescript/generated.ts | 199 ++++- reflectapi-demo/openapi.json | 517 +++++++++++- reflectapi-demo/reflectapi.json | 775 +++++++++++++++++- reflectapi-demo/src/lib.rs | 279 ++++++- ...tests__basic__reflectapi_deprecated-5.snap | 4 +- ...__basic__reflectapi_enum_documented-4.snap | 8 +- ...basic__reflectapi_struct_documented-4.snap | 4 +- ...sts__basic__reflectapi_struct_empty-5.snap | 4 +- ...i_struct_one_basic_field_static_str-4.snap | 4 +- ...ctapi_struct_one_basic_field_string-4.snap | 4 +- ..._basic_field_string_reflectapi_both-5.snap | 4 +- ...ield_string_reflectapi_both_equally-5.snap | 4 +- ...eld_string_reflectapi_both_equally2-4.snap | 4 +- ...ing_reflectapi_both_with_attributes-5.snap | 8 +- ...flectapi_struct_one_basic_field_u32-4.snap | 4 +- ...ts__basic__reflectapi_struct_option-5.snap | 4 +- ...sts__basic__reflectapi_struct_tuple-5.snap | 2 +- ...tapi_struct_with_additional_derives-5.snap | 8 +- ...ruct_with_all_primitive_type_fields-5.snap | 12 +- ...__basic__reflectapi_struct_with_arc-5.snap | 4 +- ...ectapi_struct_with_arc_pointer_only-5.snap | 4 +- ...i_struct_with_attributes_input_only-5.snap | 4 +- ..._struct_with_attributes_output_only-5.snap | 4 +- ...with_external_generic_type_fallback-5.snap | 4 +- ...ectapi_struct_with_fixed_size_array-5.snap | 6 +- ...sic__reflectapi_struct_with_hashmap-5.snap | 4 +- ...eflectapi_struct_with_hashset_field-5.snap | 6 +- ...i_struct_with_hashset_field_generic-5.snap | 4 +- ...asic__reflectapi_struct_with_nested-5.snap | 8 +- ...lectapi_struct_with_nested_external-5.snap | 8 +- ...reflectapi_struct_with_self_via_arc-5.snap | 4 +- ...__reflectapi_struct_with_skip_field-5.snap | 4 +- ...ectapi_struct_with_skip_field_input-5.snap | 8 +- ...ctapi_struct_with_skip_field_output-5.snap | 8 +- ...lectapi_struct_with_transform_array-5.snap | 6 +- ...flectapi_struct_with_transform_both-5.snap | 4 +- ...tapi_struct_with_transform_fallback-5.snap | 4 +- ...ruct_with_transform_fallback_nested-5.snap | 4 +- ...lectapi_struct_with_transform_input-5.snap | 8 +- ...ectapi_struct_with_transform_output-5.snap | 8 +- ...basic__reflectapi_struct_with_tuple-5.snap | 4 +- ...sic__reflectapi_struct_with_tuple12-5.snap | 4 +- ...__basic__reflectapi_struct_with_vec-5.snap | 6 +- ...reflectapi_struct_with_vec_external-5.snap | 8 +- ...__reflectapi_struct_with_vec_nested-5.snap | 8 +- ...sic__reflectapi_struct_with_vec_two-5.snap | 6 +- ...variant_and_fields_and_named_fields-4.snap | 8 +- ..._enum_with_empty_variant_and_fields-4.snap | 4 +- ...emo__tests__enums__enum_with_fields-4.snap | 8 +- ...o__tests__enums__enum_with_generics-4.snap | 8 +- ...nums__enum_with_generics_and_fields-4.snap | 8 +- ...enerics_and_fields_and_named_fields-4.snap | 12 +- ...lly_tagged_enum_with_tuple_variants-5.snap | 4 +- ...ally_tagged_enum_with_unit_variants-5.snap | 8 +- ...ics__struct_with_circular_reference-4.snap | 4 +- ...uct_with_circular_reference_generic-4.snap | 4 +- ...h_circular_reference_generic_parent-4.snap | 8 +- ...cular_reference_generic_without_box-4.snap | 4 +- ...eference_generic_without_box_parent-4.snap | 8 +- ...generic_without_box_parent_specific-4.snap | 8 +- ...__struct_with_nested_generic_struct-4.snap | 8 +- ...ct_with_nested_generic_struct_twice-4.snap | 8 +- ...enerics__struct_with_simple_generic-4.snap | 4 +- ...__generics__struct_with_vec_generic-4.snap | 4 +- ...cs__struct_with_vec_generic_generic-4.snap | 8 +- ...ct_with_vec_generic_generic_generic-4.snap | 16 +- ...adj_repr_enum_with_untagged_variant-5.snap | 8 +- ..._tests__serde__box_field_unwrapping-5.snap | 4 +- ...ectapi_demo__tests__serde__datetime-5.snap | 4 +- ...e__empty_variants_adjacently_tagged-5.snap | 16 +- ...e__empty_variants_externally_tagged-5.snap | 8 +- ...e__empty_variants_internally_tagged-5.snap | 8 +- ...sts__serde__empty_variants_untagged-5.snap | 12 +- ...xed_variant_types_internally_tagged-5.snap | 12 +- ...__serde__enum_rename_all_on_variant-5.snap | 8 +- ...s__serde__enum_rename_variant_field-5.snap | 4 +- ...ectapi_demo__tests__serde__enum_tag-5.snap | 8 +- ...emo__tests__serde__enum_tag_content-5.snap | 12 +- ..._serde__enum_tag_content_rename_all-5.snap | 12 +- ...i_demo__tests__serde__enum_untagged-5.snap | 8 +- ..._tests__serde__enum_with_field_skip-5.snap | 4 +- ...sts__serde__enum_with_many_variants-5.snap | 48 +- ...__enum_with_rename_to_invalid_chars-5.snap | 4 +- ..._enum_with_serde_rename_on_variants-5.snap | 16 +- ...sts__serde__enum_with_variant_other-5.snap | 12 +- ...__serde__enum_with_variant_untagged-5.snap | 4 +- ..._demo__tests__serde__external_impls-5.snap | 4 +- ...s__serde__field_all_python_keywords-5.snap | 4 +- ...rde__field_names_with_special_chars-5.snap | 4 +- ...latten_adjacently_tagged_enum_field-5.snap | 20 +- ...latten_enum_with_unit_variants_only-5.snap | 24 +- ...latten_externally_tagged_enum_field-5.snap | 12 +- ...s__serde__flatten_internally_tagged-5.snap | 16 +- ...latten_internally_tagged_enum_field-5.snap | 16 +- ...ts__serde__flatten_multiple_structs-5.snap | 12 +- ...ten_optional_internally_tagged_enum-5.snap | 16 +- ...n_struct_and_internal_enum_combined-5.snap | 20 +- ..._flatten_struct_with_nested_flatten-5.snap | 12 +- ...pi_demo__tests__serde__flatten_unit-5.snap | 8 +- ..._serde__flatten_untagged_enum_field-5.snap | 12 +- ...rde__generic_adjacently_tagged_enum-5.snap | 8 +- ...rde__generic_externally_tagged_enum-5.snap | 4 +- ..._generic_flatten_drops_inner_fields-5.snap | 12 +- ...eneric_flatten_enum_variant_typevar-5.snap | 20 +- ...rde__generic_flatten_leaf_collision-5.snap | 24 +- ...ests__serde__generic_flatten_nested-5.snap | 20 +- ...ts__serde__generic_flatten_optional-5.snap | 12 +- ..._generic_flatten_two_instantiations-5.snap | 24 +- ..._flatten_typevar_in_generic_context-5.snap | 16 +- ...atten_typevar_nested_in_generic_arg-5.snap | 16 +- ...de__generic_struct_repr_transparent-5.snap | 16 +- ...ic_with_concrete_flatten_not_marked-5.snap | 16 +- ...tapi_demo__tests__serde__kebab_case-5.snap | 4 +- ...__multiple_underscore_prefix_fields-5.snap | 4 +- ...de__namespace_deeply_nested_modules-5.snap | 4 +- ...erde__namespace_single_segment_type-5.snap | 4 +- ...serde__namespace_with_numeric_start-5.snap | 4 +- ...s__serde__nested_generic_containers-5.snap | 4 +- ...rde__nested_internally_tagged_enums-5.snap | 24 +- ...ted_internally_tagged_enums_minimal-5.snap | 8 +- ..._newtype_variants_adjacently_tagged-5.snap | 16 +- ..._newtype_variants_externally_tagged-5.snap | 12 +- ..._newtype_variants_internally_tagged-5.snap | 16 +- ...emo__tests__serde__option_of_option-5.snap | 4 +- ...sts__serde__self_referential_struct-5.snap | 4 +- ...api_demo__tests__serde__struct_from-5.snap | 8 +- ...api_demo__tests__serde__struct_into-5.snap | 8 +- ...i_demo__tests__serde__struct_rename-5.snap | 4 +- ...mo__tests__serde__struct_rename_all-5.snap | 4 +- ...erde__struct_rename_all_differently-5.snap | 8 +- ...erde__struct_rename_all_pascal_case-5.snap | 4 +- ...s__serde__struct_rename_differently-5.snap | 8 +- ...__tests__serde__struct_rename_field-5.snap | 8 +- ...repr_transparent_generic_inner_type-5.snap | 6 +- ...demo__tests__serde__struct_try_from-5.snap | 8 +- ...__tests__serde__struct_with_flatten-5.snap | 8 +- ...serde__struct_with_flatten_optional-5.snap | 8 +- ..._with_flatten_optional_and_required-5.snap | 12 +- ...struct_with_rename_to_invalid_chars-5.snap | 4 +- ...e__struct_with_rename_to_kebab_case-5.snap | 4 +- ...s__serde__struct_with_serde_default-5.snap | 8 +- ...ests__serde__struct_with_serde_skip-5.snap | 4 +- ..._struct_with_serde_skip_deserialize-5.snap | 8 +- ...e__struct_with_serde_skip_serialize-5.snap | 8 +- ...struct_with_serde_skip_serialize_if-5.snap | 8 +- ...ectapi_demo__tests__serde__timezone-5.snap | 4 +- ...ctapi_demo__tests__write_openapi_spec.snap | 517 +++++++++++- .../src/reflectapi_runtime/partial.py | 37 +- .../tests/test_codegen_regressions.py | 20 +- .../tests/test_edge_cases.py | 4 - .../tests/test_partial.py | 30 +- reflectapi/src/codegen/openapi.rs | 43 + reflectapi/src/codegen/python.rs | 108 ++- 173 files changed, 4395 insertions(+), 815 deletions(-) create mode 100644 reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py create mode 100644 reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7e9a2448..6115e273 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -40,12 +40,10 @@ jobs: run: uv run --extra dev pytest -q --no-cov # Regenerates the demo Python client from the in-repo schema and - # imports it. The generated package's strict `_rebuild_models()` runs - # at import time and raises on any dangling type reference (the class - # of bug that produced four user-reported issues in 0.17.x: - # namespace-alias mismatches, std.tuple/std.marker references with no - # backing class, etc.). If this job is green the whole class is gone; - # if it ever goes red, the codegen has regressed. + # imports it. The generated package's strict `_rebuild_models()` + # runs at import time and raises on any dangling type reference, so + # this job catches the class of bug where the codegen emits a + # Python annotation pointing at a symbol it never defined. python-codegen-smoke: name: Python codegen smoke test runs-on: ubuntu-latest diff --git a/reflectapi-demo/clients/python/api_client/_client.py b/reflectapi-demo/clients/python/api_client/_client.py index 682f13f2..f0f6a1c1 100644 --- a/reflectapi-demo/clients/python/api_client/_client.py +++ b/reflectapi-demo/clients/python/api_client/_client.py @@ -278,26 +278,48 @@ def __init__( self.pets = AsyncPetsClient(self) - async def codegen_regression( + async def codegen_coverage( self, - data: Optional[myapi.CodegenRegressionRequest] = None, - ) -> ApiResponse[myapi.CodegenRegressionResponse]: - """Regression-test endpoint pinning codegen bugs + data: Optional[myapi.coverage.CoverageRequest] = None, + ) -> ApiResponse[myapi.coverage.CoverageResponse]: + """Coverage fixtures for codegen edge cases Args: - data: Request data for the codegen_regression operation. + data: Request data for the codegen_coverage operation. Returns: - ApiResponse[myapi.CodegenRegressionResponse]: Response containing myapi.CodegenRegressionResponse data + ApiResponse[myapi.coverage.CoverageResponse]: Response containing myapi.coverage.CoverageResponse data """ - path = "/codegen-regression" + path = "/codegen-coverage" params: dict[str, Any] = {} return await self._make_request( path, params=params if params else None, json_model=data, - response_model=myapi.CodegenRegressionResponse, + response_model=myapi.coverage.CoverageResponse, + ) + + async def codegen_order_coverage( + self, + data: Optional[myapi.OrderCoverageRequest] = None, + ) -> ApiResponse[myapi.OrderCoverageResponse]: + """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering + + Args: + data: Request data for the codegen_order_coverage operation. + + Returns: + ApiResponse[myapi.OrderCoverageResponse]: Response containing myapi.OrderCoverageResponse data + """ + path = "/codegen-order-coverage" + + params: dict[str, Any] = {} + return await self._make_request( + path, + params=params if params else None, + json_model=data, + response_model=myapi.OrderCoverageResponse, ) @@ -524,24 +546,46 @@ def __init__( self.pets = PetsClient(self) - def codegen_regression( + def codegen_coverage( + self, + data: Optional[myapi.coverage.CoverageRequest] = None, + ) -> ApiResponse[myapi.coverage.CoverageResponse]: + """Coverage fixtures for codegen edge cases + + Args: + data: Request data for the codegen_coverage operation. + + Returns: + ApiResponse[myapi.coverage.CoverageResponse]: Response containing myapi.coverage.CoverageResponse data + """ + path = "/codegen-coverage" + + params: dict[str, Any] = {} + return self._make_request( + path, + params=params if params else None, + json_model=data, + response_model=myapi.coverage.CoverageResponse, + ) + + def codegen_order_coverage( self, - data: Optional[myapi.CodegenRegressionRequest] = None, - ) -> ApiResponse[myapi.CodegenRegressionResponse]: - """Regression-test endpoint pinning codegen bugs + data: Optional[myapi.OrderCoverageRequest] = None, + ) -> ApiResponse[myapi.OrderCoverageResponse]: + """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering Args: - data: Request data for the codegen_regression operation. + data: Request data for the codegen_order_coverage operation. Returns: - ApiResponse[myapi.CodegenRegressionResponse]: Response containing myapi.CodegenRegressionResponse data + ApiResponse[myapi.OrderCoverageResponse]: Response containing myapi.OrderCoverageResponse data """ - path = "/codegen-regression" + path = "/codegen-order-coverage" params: dict[str, Any] = {} return self._make_request( path, params=params if params else None, json_model=data, - response_model=myapi.CodegenRegressionResponse, + response_model=myapi.OrderCoverageResponse, ) diff --git a/reflectapi-demo/clients/python/api_client/_rebuild.py b/reflectapi-demo/clients/python/api_client/_rebuild.py index a00559d9..6480cb4b 100644 --- a/reflectapi-demo/clients/python/api_client/_rebuild.py +++ b/reflectapi-demo/clients/python/api_client/_rebuild.py @@ -12,9 +12,34 @@ from . import myapi from .myapi import ( - MyapiCodegenRegressionRequest, - MyapiCodegenRegressionResponse, MyapiHealthCheckFail, + MyapiOrderCoverageRequest, + MyapiOrderCoverageResponse, +) + +from . import myapi + +from .myapi.coverage import ( + MyapiCoverageBaseModel, + MyapiCoverageDeepOption, + MyapiCoverageDefaultedField, + MyapiCoverageEmptyStruct, + MyapiCoverageGenericTree, + MyapiCoverageIntKeyedMap, + MyapiCoverageKeywordVariants, + MyapiCoverageKeywordVariantsClass, + MyapiCoverageKeywordVariantsLambda, + MyapiCoverageKeywordVariantsReturn, + MyapiCoverageMutualA, + MyapiCoverageMutualB, + MyapiCoveragePyKeywordFields, + MyapiCoveragePydanticReservedFields, + MyapiCoverageRequest, + MyapiCoverageResponse, + MyapiCoverageShadowingFields, + MyapiCoverageTreeNode, + MyapiCoverageWeirdDocstring, + MyapiCoverageWrapper, ) from . import myapi @@ -76,6 +101,7 @@ def rebuild_models() -> None: myapi.myapi = myapi + myapi.coverage.myapi = myapi myapi.model.myapi = myapi myapi.model.input.myapi = myapi myapi.model.output.myapi = myapi @@ -84,9 +110,26 @@ def rebuild_models() -> None: reflectapi.reflectapi = reflectapi errors: list[str] = [] for _model in [ - MyapiCodegenRegressionRequest, - MyapiCodegenRegressionResponse, MyapiHealthCheckFail, + MyapiOrderCoverageRequest, + MyapiOrderCoverageResponse, + MyapiCoverageBaseModel, + MyapiCoverageRequest, + MyapiCoverageResponse, + MyapiCoverageDeepOption, + MyapiCoverageDefaultedField, + MyapiCoverageEmptyStruct, + MyapiCoverageGenericTree, + MyapiCoverageIntKeyedMap, + MyapiCoverageKeywordVariants, + MyapiCoverageMutualA, + MyapiCoverageMutualB, + MyapiCoveragePyKeywordFields, + MyapiCoveragePydanticReservedFields, + MyapiCoverageShadowingFields, + MyapiCoverageTreeNode, + MyapiCoverageWeirdDocstring, + MyapiCoverageWrapper, MyapiModelBehavior, MyapiModelKind, MyapiModelInputPet, diff --git a/reflectapi-demo/clients/python/api_client/generated.py b/reflectapi-demo/clients/python/api_client/generated.py index d58bee2e..10bd2372 100644 --- a/reflectapi-demo/clients/python/api_client/generated.py +++ b/reflectapi-demo/clients/python/api_client/generated.py @@ -16,9 +16,34 @@ from . import myapi from .myapi import ( - MyapiCodegenRegressionRequest, - MyapiCodegenRegressionResponse, MyapiHealthCheckFail, + MyapiOrderCoverageRequest, + MyapiOrderCoverageResponse, +) + +from . import myapi + +from .myapi.coverage import ( + MyapiCoverageBaseModel, + MyapiCoverageDeepOption, + MyapiCoverageDefaultedField, + MyapiCoverageEmptyStruct, + MyapiCoverageGenericTree, + MyapiCoverageIntKeyedMap, + MyapiCoverageKeywordVariants, + MyapiCoverageKeywordVariantsClass, + MyapiCoverageKeywordVariantsLambda, + MyapiCoverageKeywordVariantsReturn, + MyapiCoverageMutualA, + MyapiCoverageMutualB, + MyapiCoveragePyKeywordFields, + MyapiCoveragePydanticReservedFields, + MyapiCoverageRequest, + MyapiCoverageResponse, + MyapiCoverageShadowingFields, + MyapiCoverageTreeNode, + MyapiCoverageWeirdDocstring, + MyapiCoverageWrapper, ) from . import myapi @@ -84,8 +109,26 @@ __all__ = [ "AsyncClient", "Client", - "MyapiCodegenRegressionRequest", - "MyapiCodegenRegressionResponse", + "MyapiCoverageBaseModel", + "MyapiCoverageDeepOption", + "MyapiCoverageDefaultedField", + "MyapiCoverageEmptyStruct", + "MyapiCoverageGenericTree", + "MyapiCoverageIntKeyedMap", + "MyapiCoverageKeywordVariants", + "MyapiCoverageKeywordVariantsClass", + "MyapiCoverageKeywordVariantsLambda", + "MyapiCoverageKeywordVariantsReturn", + "MyapiCoverageMutualA", + "MyapiCoverageMutualB", + "MyapiCoveragePyKeywordFields", + "MyapiCoveragePydanticReservedFields", + "MyapiCoverageRequest", + "MyapiCoverageResponse", + "MyapiCoverageShadowingFields", + "MyapiCoverageTreeNode", + "MyapiCoverageWeirdDocstring", + "MyapiCoverageWrapper", "MyapiHealthCheckFail", "MyapiModelBehavior", "MyapiModelBehaviorAggressiveVariant", @@ -96,6 +139,8 @@ "MyapiModelKindCat", "MyapiModelKindDog", "MyapiModelOutputPet", + "MyapiOrderCoverageRequest", + "MyapiOrderCoverageResponse", "MyapiOrderInsertData", "MyapiOrderPolicy", "MyapiOrderRateLimit", diff --git a/reflectapi-demo/clients/python/api_client/myapi/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/__init__.py index df8f0ec1..b55fc7a6 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/__init__.py @@ -53,35 +53,36 @@ StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] -class MyapiCodegenRegressionRequest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) - - order: myapi.order.OrderInsertData = Field( - description="Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability." - ) - rate_limit: myapi.order.RateLimit = Field( - description="Pulls in `order::RateLimit` for Duration (bug 3)." - ) - policy: myapi.order.Policy[str, int] = Field( - description="Pulls in `order::Policy` for PhantomData (bug 4)." +class MyapiHealthCheckFail(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() ) -class MyapiCodegenRegressionResponse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) +class MyapiOrderCoverageRequest(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + order: myapi.order.OrderInsertData + rate_limit: myapi.order.RateLimit + policy: myapi.order.Policy[str, int] - ok: bool +class MyapiOrderCoverageResponse(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) -class MyapiHealthCheckFail(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + ok: bool # Public aliases for this module -CodegenRegressionRequest = MyapiCodegenRegressionRequest -CodegenRegressionResponse = MyapiCodegenRegressionResponse HealthCheckFail = MyapiHealthCheckFail +OrderCoverageRequest = MyapiOrderCoverageRequest +OrderCoverageResponse = MyapiOrderCoverageResponse +from . import coverage from . import model from . import order from . import proto @@ -94,12 +95,13 @@ class MyapiHealthCheckFail(BaseModel): pass __all__ = [ - "CodegenRegressionRequest", - "CodegenRegressionResponse", "HealthCheckFail", - "MyapiCodegenRegressionRequest", - "MyapiCodegenRegressionResponse", "MyapiHealthCheckFail", + "MyapiOrderCoverageRequest", + "MyapiOrderCoverageResponse", + "OrderCoverageRequest", + "OrderCoverageResponse", + "coverage", "model", "order", "proto", diff --git a/reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py new file mode 100644 index 00000000..08545f96 --- /dev/null +++ b/reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py @@ -0,0 +1,345 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Demo application +This is a demo application +""" + +from __future__ import annotations + + +# Standard library imports +import warnings +from collections.abc import AsyncIterator, Iterator +from datetime import datetime +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import ( + BaseModel, + ConfigDict, + Field, + RootModel, + model_serializer, + model_validator, +) + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel +from reflectapi_runtime import ( + parse_externally_tagged as _parse_externally_tagged, + serialize_externally_tagged as _serialize_externally_tagged, +) + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class MyapiAdversarialRequest(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + keywords: myapi.adversarial.PyKeywordFields + reserved: myapi.adversarial.PydanticReservedFields + tree: myapi.adversarial.TreeNode + mutual: myapi.adversarial.MutualA + generic_tree: myapi.adversarial.GenericTree[int] + keyword_variants: myapi.adversarial.KeywordVariants + int_keyed: myapi.adversarial.IntKeyedMap + user_id: int + deep_option: myapi.adversarial.DeepOption + shadowing: myapi.adversarial.ShadowingFields + empty: myapi.adversarial.EmptyStruct + weird_doc: myapi.adversarial.WeirdDocstring + shadow_base_model: myapi.adversarial.BaseModel + wrapper_int: myapi.adversarial.Wrapper[int] + wrapper_str: myapi.adversarial.Wrapper[str] + defaulted: myapi.adversarial.DefaultedField + + +class MyapiAdversarialResponse(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + ok: bool + + +class MyapiAdversarialBaseModel(BaseModel): + """The generated module imports `BaseModel`, `Field`, `Annotated`, + `Generic`, `TypeVar` from `pydantic`/`typing`. A user struct + with one of those names must not break the module.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + label: str + + +class MyapiAdversarialDeepOption(ReflectapiPartialModel): + model_config = ConfigDict( + extra="ignore", + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + maybe_maybe: str | None | None = None + + +class MyapiAdversarialDefaultedField(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + count: int | None = None + + +class MyapiAdversarialEmptyStruct(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + +class MyapiAdversarialGenericTree(BaseModel, Generic[T]): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: T + children: list[myapi.adversarial.GenericTree[T]] + + +class MyapiAdversarialIntKeyedMap(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + by_id: dict[int, str] + uuid_keyed: dict[str, str] + + +class MyapiAdversarialMutualA(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + b: myapi.adversarial.MutualB | None = None + + +class MyapiAdversarialMutualB(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + a: myapi.adversarial.MutualA | None = None + + +class MyapiAdversarialPyKeywordFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + class_: str = Field(serialization_alias="class", validation_alias="class") + from_: int = Field(serialization_alias="from", validation_alias="from") + import_: bool = Field(serialization_alias="import", validation_alias="import") + lambda_: int = Field(serialization_alias="lambda", validation_alias="lambda") + return_: list[int] = Field(serialization_alias="return", validation_alias="return") + yield_: str | None = Field( + default=None, serialization_alias="yield", validation_alias="yield" + ) + none: int | None = Field( + default=None, serialization_alias="None", validation_alias="None" + ) + true: bool = Field(serialization_alias="True", validation_alias="True") + type_: str = Field(serialization_alias="type", validation_alias="type") + match: str + + +class MyapiAdversarialPydanticReservedFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + model_config_: str = Field( + serialization_alias="model_config", validation_alias="model_config" + ) + model_fields_set_: str = Field( + serialization_alias="model_fields_set", validation_alias="model_fields_set" + ) + model_dump_json_: str = Field( + serialization_alias="model_dump_json", validation_alias="model_dump_json" + ) + + +class MyapiAdversarialShadowingFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + field: str + annotated: str + generic: str + base_model: str + + +class MyapiAdversarialTreeNode(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: str + children: list[myapi.adversarial.TreeNode] + parent: myapi.adversarial.TreeNode | None = None + + +class MyapiAdversarialWeirdDocstring(BaseModel): + """A docstring with \\"quotes\\" and \\'apostrophes\\' and a backslash: \\\\\\\\ +And a \\"\\"\\"triple quote\\"\\"\\" inside.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: str + + +class MyapiAdversarialWrapper(BaseModel, Generic[T]): + """Each monomorphisation must produce a distinct Python class. + Shared-name collapse would mean the second usage\\'s fields would + silently shadow the first.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + inner: T + + +class MyapiAdversarialKeywordVariantsClass(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["class"] = Field(default="class", description="Discriminator field") + + +class MyapiAdversarialKeywordVariantsLambda(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["lambda"] = Field(default="lambda", description="Discriminator field") + name: str + + +class MyapiAdversarialKeywordVariantsReturn(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["return"] = Field(default="return", description="Discriminator field") + value: int + + +class MyapiAdversarialKeywordVariants(RootModel): + root: Annotated[ + Union[ + MyapiAdversarialKeywordVariantsClass, + MyapiAdversarialKeywordVariantsLambda, + MyapiAdversarialKeywordVariantsReturn, + ], + Field(discriminator="kind"), + ] + + +# Public aliases for this module +AdversarialRequest = MyapiAdversarialRequest +AdversarialResponse = MyapiAdversarialResponse +BaseModel = MyapiAdversarialBaseModel +DeepOption = MyapiAdversarialDeepOption +DefaultedField = MyapiAdversarialDefaultedField +EmptyStruct = MyapiAdversarialEmptyStruct +GenericTree = MyapiAdversarialGenericTree +IntKeyedMap = MyapiAdversarialIntKeyedMap +KeywordVariants = MyapiAdversarialKeywordVariants +KeywordVariantsClass = MyapiAdversarialKeywordVariantsClass +KeywordVariantsLambda = MyapiAdversarialKeywordVariantsLambda +KeywordVariantsReturn = MyapiAdversarialKeywordVariantsReturn +MutualA = MyapiAdversarialMutualA +MutualB = MyapiAdversarialMutualB +PyKeywordFields = MyapiAdversarialPyKeywordFields +PydanticReservedFields = MyapiAdversarialPydanticReservedFields +Request = MyapiAdversarialRequest +Response = MyapiAdversarialResponse +ShadowingFields = MyapiAdversarialShadowingFields +TreeNode = MyapiAdversarialTreeNode +WeirdDocstring = MyapiAdversarialWeirdDocstring +Wrapper = MyapiAdversarialWrapper + +__all__ = [ + "AdversarialRequest", + "AdversarialResponse", + "BaseModel", + "DeepOption", + "DefaultedField", + "EmptyStruct", + "GenericTree", + "IntKeyedMap", + "KeywordVariants", + "KeywordVariantsClass", + "KeywordVariantsLambda", + "KeywordVariantsReturn", + "MutualA", + "MutualB", + "MyapiAdversarialBaseModel", + "MyapiAdversarialDeepOption", + "MyapiAdversarialDefaultedField", + "MyapiAdversarialEmptyStruct", + "MyapiAdversarialGenericTree", + "MyapiAdversarialIntKeyedMap", + "MyapiAdversarialKeywordVariants", + "MyapiAdversarialKeywordVariantsClass", + "MyapiAdversarialKeywordVariantsLambda", + "MyapiAdversarialKeywordVariantsReturn", + "MyapiAdversarialMutualA", + "MyapiAdversarialMutualB", + "MyapiAdversarialPyKeywordFields", + "MyapiAdversarialPydanticReservedFields", + "MyapiAdversarialRequest", + "MyapiAdversarialResponse", + "MyapiAdversarialShadowingFields", + "MyapiAdversarialTreeNode", + "MyapiAdversarialWeirdDocstring", + "MyapiAdversarialWrapper", + "PyKeywordFields", + "PydanticReservedFields", + "Request", + "Response", + "ShadowingFields", + "TreeNode", + "WeirdDocstring", + "Wrapper", +] diff --git a/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py new file mode 100644 index 00000000..a1bb3d11 --- /dev/null +++ b/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py @@ -0,0 +1,337 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Demo application +This is a demo application +""" + +from __future__ import annotations + + +# Standard library imports +import warnings +from collections.abc import AsyncIterator, Iterator +from datetime import datetime +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import ( + BaseModel, + ConfigDict, + Field, + RootModel, + model_serializer, + model_validator, +) + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel +from reflectapi_runtime import ( + parse_externally_tagged as _parse_externally_tagged, + serialize_externally_tagged as _serialize_externally_tagged, +) + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class MyapiCoverageBaseModel(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + label: str + + +class MyapiCoverageRequest(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + keywords: myapi.coverage.PyKeywordFields + reserved: myapi.coverage.PydanticReservedFields + tree: myapi.coverage.TreeNode + mutual: myapi.coverage.MutualA + generic_tree: myapi.coverage.GenericTree[int] + keyword_variants: myapi.coverage.KeywordVariants + int_keyed: myapi.coverage.IntKeyedMap + user_id: int + deep_option: myapi.coverage.DeepOption + shadowing: myapi.coverage.ShadowingFields + empty: myapi.coverage.EmptyStruct + weird_doc: myapi.coverage.WeirdDocstring + shadow_base_model: myapi.coverage.BaseModel + wrapper_int: myapi.coverage.Wrapper[int] + wrapper_str: myapi.coverage.Wrapper[str] + defaulted: myapi.coverage.DefaultedField + + +class MyapiCoverageResponse(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + ok: bool + + +class MyapiCoverageDeepOption(ReflectapiPartialModel): + model_config = ConfigDict( + extra="ignore", + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + maybe_maybe: str | None | None = None + + +class MyapiCoverageDefaultedField(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + count: int | None = None + + +class MyapiCoverageEmptyStruct(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + +class MyapiCoverageGenericTree(BaseModel, Generic[T]): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: T + children: list[myapi.coverage.GenericTree[T]] + + +class MyapiCoverageIntKeyedMap(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + by_id: dict[int, str] + uuid_keyed: dict[str, str] + + +class MyapiCoverageMutualA(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + b: myapi.coverage.MutualB | None = None + + +class MyapiCoverageMutualB(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + a: myapi.coverage.MutualA | None = None + + +class MyapiCoveragePyKeywordFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + class_: str = Field(serialization_alias="class", validation_alias="class") + from_: int = Field(serialization_alias="from", validation_alias="from") + import_: bool = Field(serialization_alias="import", validation_alias="import") + lambda_: int = Field(serialization_alias="lambda", validation_alias="lambda") + return_: list[int] = Field(serialization_alias="return", validation_alias="return") + yield_: str | None = Field( + default=None, serialization_alias="yield", validation_alias="yield" + ) + none: int | None = Field( + default=None, serialization_alias="None", validation_alias="None" + ) + true: bool = Field(serialization_alias="True", validation_alias="True") + type_: str = Field(serialization_alias="type", validation_alias="type") + match: str + + +class MyapiCoveragePydanticReservedFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + model_config_: str = Field( + serialization_alias="model_config", validation_alias="model_config" + ) + model_fields_set_: str = Field( + serialization_alias="model_fields_set", validation_alias="model_fields_set" + ) + model_dump_json_: str = Field( + serialization_alias="model_dump_json", validation_alias="model_dump_json" + ) + + +class MyapiCoverageShadowingFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + field: str + annotated: str + generic: str + base_model: str + + +class MyapiCoverageTreeNode(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: str + children: list[myapi.coverage.TreeNode] + parent: myapi.coverage.TreeNode | None = None + + +class MyapiCoverageWeirdDocstring(BaseModel): + """A docstring with \\"quotes\\" and \\'apostrophes\\' and a backslash: \\\\\\\\ +And a \\"\\"\\"triple quote\\"\\"\\" inside.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: str + + +class MyapiCoverageWrapper(BaseModel, Generic[T]): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + inner: T + + +class MyapiCoverageKeywordVariantsClass(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["class"] = Field(default="class", description="Discriminator field") + + +class MyapiCoverageKeywordVariantsLambda(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["lambda"] = Field(default="lambda", description="Discriminator field") + name: str + + +class MyapiCoverageKeywordVariantsReturn(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["return"] = Field(default="return", description="Discriminator field") + value: int + + +class MyapiCoverageKeywordVariants(RootModel): + root: Annotated[ + Union[ + MyapiCoverageKeywordVariantsClass, + MyapiCoverageKeywordVariantsLambda, + MyapiCoverageKeywordVariantsReturn, + ], + Field(discriminator="kind"), + ] + + +# Public aliases for this module +BaseModel = MyapiCoverageBaseModel +CoverageRequest = MyapiCoverageRequest +CoverageResponse = MyapiCoverageResponse +DeepOption = MyapiCoverageDeepOption +DefaultedField = MyapiCoverageDefaultedField +EmptyStruct = MyapiCoverageEmptyStruct +GenericTree = MyapiCoverageGenericTree +IntKeyedMap = MyapiCoverageIntKeyedMap +KeywordVariants = MyapiCoverageKeywordVariants +KeywordVariantsClass = MyapiCoverageKeywordVariantsClass +KeywordVariantsLambda = MyapiCoverageKeywordVariantsLambda +KeywordVariantsReturn = MyapiCoverageKeywordVariantsReturn +MutualA = MyapiCoverageMutualA +MutualB = MyapiCoverageMutualB +PyKeywordFields = MyapiCoveragePyKeywordFields +PydanticReservedFields = MyapiCoveragePydanticReservedFields +Request = MyapiCoverageRequest +Response = MyapiCoverageResponse +ShadowingFields = MyapiCoverageShadowingFields +TreeNode = MyapiCoverageTreeNode +WeirdDocstring = MyapiCoverageWeirdDocstring +Wrapper = MyapiCoverageWrapper + +__all__ = [ + "BaseModel", + "CoverageRequest", + "CoverageResponse", + "DeepOption", + "DefaultedField", + "EmptyStruct", + "GenericTree", + "IntKeyedMap", + "KeywordVariants", + "KeywordVariantsClass", + "KeywordVariantsLambda", + "KeywordVariantsReturn", + "MutualA", + "MutualB", + "MyapiCoverageBaseModel", + "MyapiCoverageDeepOption", + "MyapiCoverageDefaultedField", + "MyapiCoverageEmptyStruct", + "MyapiCoverageGenericTree", + "MyapiCoverageIntKeyedMap", + "MyapiCoverageKeywordVariants", + "MyapiCoverageKeywordVariantsClass", + "MyapiCoverageKeywordVariantsLambda", + "MyapiCoverageKeywordVariantsReturn", + "MyapiCoverageMutualA", + "MyapiCoverageMutualB", + "MyapiCoveragePyKeywordFields", + "MyapiCoveragePydanticReservedFields", + "MyapiCoverageRequest", + "MyapiCoverageResponse", + "MyapiCoverageShadowingFields", + "MyapiCoverageTreeNode", + "MyapiCoverageWeirdDocstring", + "MyapiCoverageWrapper", + "PyKeywordFields", + "PydanticReservedFields", + "Request", + "Response", + "ShadowingFields", + "TreeNode", + "WeirdDocstring", + "Wrapper", +] diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py index 60625491..d2cf03c0 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py @@ -56,7 +56,9 @@ class MyapiModelBehaviorAggressiveVariant(BaseModel): """Aggressive variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: float = Field(description="aggressiveness level") field_1: str = Field(description="some notes") @@ -65,7 +67,9 @@ class MyapiModelBehaviorAggressiveVariant(BaseModel): class MyapiModelBehaviorOtherVariant(BaseModel): """Other variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) description: str = Field(description="Custom provided description of a behavior") notes: str | None = Field( @@ -124,7 +128,9 @@ def _serialize(self): class MyapiModelKindDog(BaseModel): """A dog""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["dog"] = Field(default="dog", description="Discriminator field") breed: str = Field(description="breed of the dog") @@ -133,7 +139,9 @@ class MyapiModelKindDog(BaseModel): class MyapiModelKindCat(BaseModel): """A cat""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["cat"] = Field(default="cat", description="Discriminator field") lives: int = Field(description="lives left") @@ -142,7 +150,9 @@ class MyapiModelKindCat(BaseModel): class MyapiModelKindBird(BaseModel): """Test for unit variants in internally tagged enums""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["bird"] = Field(default="bird", description="Discriminator field") diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py index e4f14188..451525ab 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py @@ -54,7 +54,9 @@ class MyapiModelInputPet(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str = Field(description="identity") kind: myapi.model.Kind = Field(description="kind of pet") diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py index 4d17e818..d759ffec 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py @@ -54,7 +54,9 @@ class MyapiModelOutputPet(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str = Field(description="identity") kind: myapi.model.Kind = Field(description="kind of pet") diff --git a/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py index 1b690887..3c760bdc 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/order/__init__.py @@ -54,39 +54,39 @@ class MyapiOrderInsertData(BaseModel): - """Bug 1 (namespace alias mismatch). The struct name starts with - the parent namespace\\'s cap (`Order…`) — the alias-stripping - pass used to strip the leading cap from the namespace alias - (`order.InsertData = OrderInsertData`) while field annotations - kept the un-stripped form, producing `order.OrderInsertData` - which doesn\\'t resolve at `model_rebuild()` time.""" + """A struct whose name begins with the parent namespace cap. + Exercises the namespace-alias path for both the stripped + (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) + forms.""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) identity: str alternative_part_number: tuple[str, str] | None = Field( - default=None, - description="Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + default=None, description="Exercises the `tuple[A, B]` rendering for `(A, B)`." ) class MyapiOrderPolicy(BaseModel, Generic[C, T]): - """Bug 4 (PhantomData). PhantomData has no wire data — serde - skips it. The codegen used to emit `std.marker.PhantomData[T]` - as a field annotation, leaving a dangling reference.""" + """Exercises `PhantomData` elision (the field carries no wire + data and must not appear in the Python model).""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str class MyapiOrderRateLimit(BaseModel): - """Bug 3 (Duration shape). serde emits `Duration` as - `{\\"secs\\": , \\"nanos\\": }`. Pydantic\\'s `timedelta` - validator rejects that shape, so any response with a Duration - failed validation.""" + """Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire + adapter.""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) retry_after: ReflectapiDuration max_wait: ReflectapiDuration | None = None diff --git a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py index 8c808cbe..a4b89e9f 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py @@ -54,40 +54,53 @@ class MyapiProtoHeaders(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) authorization: str = Field(description="Authorization header") class MyapiProtoInternalError(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) message: str class MyapiProtoPaginated(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) items: list[T] = Field(description="slice of a collection") cursor: str | None = Field(default=None, description="cursor for getting next page") class MyapiProtoPetsListRequest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) limit: int | None = None cursor: str | None = None class MyapiProtoPetsRemoveRequest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str = Field(description="identity") class MyapiProtoPetsUpdateRequest(ReflectapiPartialModel): model_config = ConfigDict( - extra="ignore", populate_by_name=True, validate_assignment=True + extra="ignore", + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), ) name: str = Field(description="identity") @@ -103,7 +116,9 @@ class MyapiProtoPetsUpdateRequest(ReflectapiPartialModel): class MyapiProtoValidationA(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) message: str @@ -111,7 +126,9 @@ class MyapiProtoValidationA(BaseModel): class MyapiProtoPetsCreateErrorInvalidIdentityVariant(BaseModel): """InvalidIdentity variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) message: str @@ -165,7 +182,9 @@ def _serialize(self): class MyapiProtoPetsListErrorInvalidCursor(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["InvalidCursor"] = Field( default="InvalidCursor", description="Discriminator field" @@ -173,7 +192,9 @@ class MyapiProtoPetsListErrorInvalidCursor(BaseModel): class MyapiProtoPetsListErrorUnauthorized(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["Unauthorized"] = Field( default="Unauthorized", description="Discriminator field" @@ -181,7 +202,9 @@ class MyapiProtoPetsListErrorUnauthorized(BaseModel): class MyapiProtoPetsListErrorInternal(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["Internal"] = Field( default="Internal", description="Discriminator field" @@ -208,7 +231,9 @@ class MyapiProtoPetsRemoveError(str, Enum): class MyapiProtoPetsUpdateErrorValidationVariant(BaseModel): """Validation variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: list[myapi.proto.ValidationError] @@ -262,7 +287,9 @@ def _serialize(self): class MyapiProtoValidationErrorValidationAVariant(BaseModel): """ValidationA variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: myapi.proto.ValidationA diff --git a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py index f73a18b9..fa1e6bd6 100644 --- a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py +++ b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py @@ -56,19 +56,25 @@ class ReflectapiOptionUndefined(BaseModel): """The value is missing, i.e. undefined in JavaScript""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiOptionNone(BaseModel): """The value is provided but set to none, i.e. null in JavaScript""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiOptionSome(BaseModel): """The value is provided and set to some value""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) value: T | None = None diff --git a/reflectapi-demo/clients/python/tests/conftest.py b/reflectapi-demo/clients/python/tests/conftest.py index b5b75526..08b3e7bd 100644 --- a/reflectapi-demo/clients/python/tests/conftest.py +++ b/reflectapi-demo/clients/python/tests/conftest.py @@ -24,7 +24,6 @@ def pytest_collection_modifyitems(config, items): from typing import Any -from reflectapi_runtime import ReflectapiOption from api_client.myapi.model import ( Kind as PetKind, KindDog as PetKindDog, @@ -90,12 +89,12 @@ def sample_pet_details(sample_cat: PetKindCat) -> PetDetails: @pytest.fixture def sample_update_request() -> PetsUpdateRequest: - """Create a sample PetsUpdateRequest with ReflectapiOption fields.""" + """Create a sample PetsUpdateRequest with every partial field set.""" return PetsUpdateRequest( name="TestPet", kind=PetKindDog(type="dog", breed="Labrador"), - age=ReflectapiOption(5), - behaviors=ReflectapiOption([calm_behavior()]), + age=5, + behaviors=[calm_behavior()], ) @@ -167,11 +166,9 @@ def create_update_request( request = PetsUpdateRequest(name=name) if with_age: - request.age = ReflectapiOption(kwargs.get("age", 5)) + request.age = kwargs.get("age", 5) if with_behaviors: - request.behaviors = ReflectapiOption( - kwargs.get("behaviors", [calm_behavior()]) - ) + request.behaviors = kwargs.get("behaviors", [calm_behavior()]) return request @@ -206,38 +203,30 @@ def assert_petkind_cat(pet_kind: PetKind, expected_lives: int) -> None: assert pet_kind.lives == expected_lives -def assert_reflectapi_option_some( - option: ReflectapiOption, expected_value: Any -) -> None: - """Assert that a ReflectapiOption contains the expected value.""" - assert option.is_some - assert not option.is_undefined - assert not option.is_none - assert option.unwrap() == expected_value +def assert_partial_field_value(model: Any, field: str, expected: Any) -> None: + """Assert that a partial field is set and matches ``expected``.""" + assert field in model.model_fields_set, f"field {field!r} not set" + assert getattr(model, field) == expected -def assert_reflectapi_option_undefined(option: ReflectapiOption) -> None: - """Assert that a ReflectapiOption is undefined.""" - assert option.is_undefined - assert not option.is_some - assert not option.is_none +def assert_partial_field_unset(model: Any, field: str) -> None: + """Assert that a partial field was never explicitly set on the wire.""" + assert field not in model.model_fields_set, f"field {field!r} was set" -def assert_reflectapi_option_none(option: ReflectapiOption) -> None: - """Assert that a ReflectapiOption is explicitly None.""" - assert option.is_none - assert not option.is_undefined - assert not option.is_some - assert option.value is None +def assert_partial_field_null(model: Any, field: str) -> None: + """Assert that a partial field was explicitly set to ``None``.""" + assert field in model.model_fields_set, f"field {field!r} not set" + assert getattr(model, field) is None # Export test helpers for use in test modules __all__ = [ "assert_petkind_dog", "assert_petkind_cat", - "assert_reflectapi_option_some", - "assert_reflectapi_option_undefined", - "assert_reflectapi_option_none", + "assert_partial_field_value", + "assert_partial_field_unset", + "assert_partial_field_null", "aggressive_behavior", "calm_behavior", "other_behavior", diff --git a/reflectapi-demo/clients/python/tests/generated_code/test_models.py b/reflectapi-demo/clients/python/tests/generated_code/test_models.py index f6339fe4..10ea3888 100644 --- a/reflectapi-demo/clients/python/tests/generated_code/test_models.py +++ b/reflectapi-demo/clients/python/tests/generated_code/test_models.py @@ -19,8 +19,6 @@ MyapiProtoHeaders as Headers, MyapiProtoPaginated as Paginated, ) -from reflectapi_runtime import ReflectapiOption as Option -from reflectapi_runtime import ReflectapiOption, Undefined from tests.model_helpers import root_value # For externally tagged enums, unit variants are just string literals @@ -122,40 +120,26 @@ def test_behavior_values(self): assert aggressive == {"Aggressive": [5.0, "notes"]} assert other == {"Other": {"description": "Custom", "notes": "Some notes"}} - def test_option_values(self): - """Test ReflectapiOption creation.""" - # ReflectapiOption is not an enum anymore, it's a wrapper class - from reflectapi_runtime import ReflectapiOption, Undefined +class TestPartialFields: + """Three-state partial fields on update requests.""" - opt_some = ReflectapiOption(42) - opt_none = ReflectapiOption(None) - opt_undefined = ReflectapiOption(Undefined) - - assert opt_some.is_some - assert opt_none.is_none - assert opt_undefined.is_undefined - - -class TestReflectapiOption: - """Test ReflectapiOption functionality.""" - - def test_reflectapi_option_undefined(self): - """Test ReflectapiOption with undefined value.""" - request = PetsUpdateRequest(name="test", age=ReflectapiOption(Undefined)) + def test_unset_field_not_in_fields_set(self): + request = PetsUpdateRequest(name="test") # `age` left unset assert request.name == "test" - assert request.age._value is Undefined + assert request.age is None + assert "age" not in request.model_fields_set - def test_reflectapi_option_none(self): - """Test ReflectapiOption with None value.""" - request = PetsUpdateRequest(name="test", age=ReflectapiOption(None)) + def test_explicit_none_is_in_fields_set(self): + request = PetsUpdateRequest(name="test", age=None) assert request.name == "test" - assert request.age._value is None + assert request.age is None + assert "age" in request.model_fields_set - def test_reflectapi_option_some_value(self): - """Test ReflectapiOption with actual value.""" - request = PetsUpdateRequest(name="test", age=ReflectapiOption(5)) + def test_value_is_in_fields_set(self): + request = PetsUpdateRequest(name="test", age=5) assert request.name == "test" - assert request.age._value == 5 + assert request.age == 5 + assert "age" in request.model_fields_set class TestGenericModels: @@ -233,20 +217,16 @@ def test_pet_deserialization(self): assert kind.breed == "Golden Retriever" assert pet.age == 2 - def test_reflectapi_option_serialization(self): - """Test ReflectapiOption serialization with current behavior.""" + def test_partial_field_serialization(self): + """Unset partial fields are omitted; set ones round-trip.""" request = PetsUpdateRequest( name="test", - age=ReflectapiOption(Undefined), - behaviors=ReflectapiOption([BehaviorCalm]), + behaviors=[BehaviorCalm], + # `age` left unset ) data = request.model_dump() - # Note: Current implementation includes ReflectapiOption objects as-is - # This should be improved to properly serialize the inner values - assert "age" in data - assert "behaviors" in data - assert isinstance(data["age"], ReflectapiOption) - assert isinstance(data["behaviors"], ReflectapiOption) + assert data == {"name": "test", "behaviors": [BehaviorCalm]} + assert "age" not in data class TestHeaders: diff --git a/reflectapi-demo/clients/python/tests/generated_code/test_rust_type_mappings.py b/reflectapi-demo/clients/python/tests/generated_code/test_rust_type_mappings.py index b6dd0903..99cd22a2 100644 --- a/reflectapi-demo/clients/python/tests/generated_code/test_rust_type_mappings.py +++ b/reflectapi-demo/clients/python/tests/generated_code/test_rust_type_mappings.py @@ -306,5 +306,6 @@ def test_conditional_imports(self): if has_datetime_usage: assert "from datetime import datetime" in content - # ReflectapiOption should be imported since it's used - assert "from reflectapi_runtime import ReflectapiOption" in content + # Partial models use `ReflectapiPartialModel`; the import + # should be present whenever the schema has a partial field. + assert "from reflectapi_runtime import ReflectapiPartialModel" in content diff --git a/reflectapi-demo/clients/python/tests/integration/test_integration_edge_cases.py b/reflectapi-demo/clients/python/tests/integration/test_integration_edge_cases.py index fc4a1c26..b601b87c 100644 --- a/reflectapi-demo/clients/python/tests/integration/test_integration_edge_cases.py +++ b/reflectapi-demo/clients/python/tests/integration/test_integration_edge_cases.py @@ -22,8 +22,6 @@ MyapiProtoPaginated as Paginated, ) from reflectapi_runtime import ( - ReflectapiOption, - Undefined, ApplicationError, NetworkError, TimeoutError, @@ -136,69 +134,38 @@ async def test_client_with_large_response_payload(self): @pytest.mark.integration -class TestReflectapiOptionIntegrationEdgeCases: - """Test ReflectapiOption integration edge cases.""" +class TestPartialFieldIntegration: + """Three-state partial field handling on update requests.""" - def test_update_request_with_all_option_states(self): - """Test update request with all possible ReflectapiOption states.""" - # Create request with various option states + def test_update_request_with_set_field_and_absent_field(self): request = PetsUpdateRequest( name="Option Test Pet", kind=PetKindDog(type="dog", breed="Option Breed"), - age=ReflectapiOption(5), # Some value - behaviors=ReflectapiOption(Undefined), # Undefined + age=5, + # `behaviors` left absent ) - # Use JSON dump with exclude_unset to drop undefined fields - json_data = json.loads(request.model_dump_json(exclude_unset=True)) + json_data = json.loads(request.model_dump_json()) - # Should include defined fields assert json_data["name"] == "Option Test Pet" assert json_data["kind"]["type"] == "dog" assert json_data["age"] == 5 + # Field not set → omitted from wire payload entirely + assert "behaviors" not in json_data - # Undefined fields are serialized as null - assert "behaviors" in json_data - assert json_data["behaviors"] is None - - def test_option_with_none_vs_undefined_serialization(self): - """Test distinction between None and Undefined in serialization.""" - # Request with None value - request_with_none = PetsUpdateRequest( - name="None Test", - age=ReflectapiOption(None), # Explicit None - ) - - # Request with undefined value - request_undefined = PetsUpdateRequest( - name="Undefined Test", - age=ReflectapiOption(Undefined), # Explicit Undefined - ) + def test_distinguishes_explicit_null_from_absent(self): + request_with_none = PetsUpdateRequest(name="None Test", age=None) + request_default = PetsUpdateRequest(name="Default Test") - # Request with default (should be undefined) - request_default = PetsUpdateRequest( - name="Default Test" - # age field left at default - ) - - # Test serialization differences none_json = json.loads(request_with_none.model_dump_json()) - undefined_json = json.loads( - request_undefined.model_dump_json(exclude_unset=True) - ) - default_json = json.loads(request_default.model_dump_json(exclude_unset=True)) + default_json = json.loads(request_default.model_dump_json()) - # None should be included as null - assert "age" in none_json + # Explicit None lands on the wire as null … assert none_json["age"] is None - - # Undefined should be serialized as null to match server semantics - assert "age" in undefined_json - assert undefined_json["age"] is None + # … and an unset field is omitted entirely. assert "age" not in default_json - def test_option_with_complex_nested_types(self): - """Test ReflectapiOption with complex nested behavior data.""" + def test_large_behavior_list_round_trip(self): from tests.package_imports import ( MyapiModelBehavior as Behavior, MyapiModelBehaviorAggressiveVariant as BehaviorAggressive, @@ -209,17 +176,15 @@ def test_option_with_complex_nested_types(self): Behavior("Calm"), Behavior(BehaviorAggressive(field_0=1.0, field_1="test")), Behavior(BehaviorOther(description="Other")), - ] * 100 # 300 behaviors + ] * 100 request = PetsUpdateRequest( - name="Complex Behaviors", behaviors=ReflectapiOption(complex_behaviors) + name="Complex Behaviors", behaviors=complex_behaviors ) - # Should handle large behavior lists - assert request.behaviors.is_some - assert len(request.behaviors.unwrap()) == 300 + assert request.behaviors is not None + assert len(request.behaviors) == 300 - # Should serialize properly json_data = json.loads(request.model_dump_json()) assert len(json_data["behaviors"]) == 300 diff --git a/reflectapi-demo/clients/python/tests/integration/test_performance.py b/reflectapi-demo/clients/python/tests/integration/test_performance.py index 85fbd9e0..7d5dd902 100644 --- a/reflectapi-demo/clients/python/tests/integration/test_performance.py +++ b/reflectapi-demo/clients/python/tests/integration/test_performance.py @@ -11,10 +11,9 @@ MyapiModelKindCat as PetKindCat, MyapiModelBehavior as Behavior, MyapiModelBehaviorAggressiveVariant as BehaviorAggressive, + MyapiProtoPetsUpdateRequest as PetsUpdateRequest, AsyncClient, ) -from reflectapi_runtime import ReflectapiOption - # For externally tagged enums, unit variants are just string literals BehaviorCalm = "Calm" @@ -98,34 +97,26 @@ def test_deserialization_performance(self): assert duration < 0.1 # Should deserialize 100 pets in under 0.1 seconds print(f"Deserialized 100 pets in {duration:.3f} seconds") - def test_reflectapi_option_performance(self): - """Test performance of ReflectapiOption operations.""" - from reflectapi_runtime import ReflectapiOption, Undefined - + def test_partial_model_serialization_performance(self): + """Building and dumping partial-model instances stays fast.""" start_time = time.time() - # Create many ReflectapiOption instances - options = [] + requests = [] for i in range(1000): if i % 3 == 0: - option = ReflectapiOption(i) # Some value + requests.append(PetsUpdateRequest(name=str(i), age=i)) elif i % 3 == 1: - option = ReflectapiOption(None) # Explicit None + requests.append(PetsUpdateRequest(name=str(i), age=None)) else: - option = ReflectapiOption(Undefined) # Undefined - options.append(option) - - # Test operations - some_count = sum(1 for opt in options if opt.is_some) - none_count = sum(1 for opt in options if opt.is_none) - undefined_count = sum(1 for opt in options if opt.is_undefined) + requests.append(PetsUpdateRequest(name=str(i))) # `age` unset + dumps = [r.model_dump_json() for r in requests] end_time = time.time() duration = end_time - start_time - assert some_count + none_count + undefined_count == 1000 - assert duration < 0.1 # Should handle 1000 operations in under 0.1 seconds - print(f"Processed 1000 ReflectapiOption instances in {duration:.3f} seconds") + assert len(dumps) == 1000 + assert duration < 0.5 + print(f"Built and dumped 1000 partial models in {duration:.3f} seconds") @pytest.mark.slow diff --git a/reflectapi-demo/clients/python/tests/integration/test_tagged_enums_e2e.py b/reflectapi-demo/clients/python/tests/integration/test_tagged_enums_e2e.py index 37184e05..91ec2e29 100644 --- a/reflectapi-demo/clients/python/tests/integration/test_tagged_enums_e2e.py +++ b/reflectapi-demo/clients/python/tests/integration/test_tagged_enums_e2e.py @@ -19,7 +19,6 @@ MyapiProtoHeaders as Headers, MyapiProtoPaginated as Paginated, ) -from reflectapi_runtime import ReflectapiOption from tests.model_helpers import root_value # For externally tagged enums, unit variants are just string literals @@ -177,13 +176,13 @@ class TestTaggedEnumInRequests: def test_update_request_with_dog_kind(self): """Test PetsUpdateRequest with dog kind.""" dog = PetKindDog(type="dog", breed="Retriever") - request = PetsUpdateRequest(name="Buddy", kind=dog, age=ReflectapiOption(6)) + request = PetsUpdateRequest(name="Buddy", kind=dog, age=6) assert request.name == "Buddy" kind = root_value(request.kind) assert kind.type == "dog" assert kind.breed == "Retriever" - assert request.age._value == 6 + assert request.age == 6 def test_update_request_serialization_with_tagged_enum(self): """Test serializing update request with tagged enum.""" diff --git a/reflectapi-demo/clients/rust/generated/src/generated.rs b/reflectapi-demo/clients/rust/generated/src/generated.rs index fb376ee0..36d99376 100644 --- a/reflectapi-demo/clients/rust/generated/src/generated.rs +++ b/reflectapi-demo/clients/rust/generated/src/generated.rs @@ -27,19 +27,31 @@ pub mod interface { client, } } - /// Regression-test endpoint pinning codegen bugs - #[tracing::instrument(name = "/codegen-regression", skip(self, headers))] - pub async fn codegen_regression( + /// Coverage fixtures for namespace/tuple/Duration/PhantomData rendering + #[tracing::instrument(name = "/codegen-order-coverage", skip(self, headers))] + pub async fn codegen_order_coverage( &self, - input: super::types::myapi::CodegenRegressionRequest, + input: super::types::myapi::OrderCoverageRequest, headers: reflectapi::Empty, ) -> Result< - super::types::myapi::CodegenRegressionResponse, + super::types::myapi::OrderCoverageResponse, reflectapi::rt::Error, > { - reflectapi::rt::__request_impl(&self.client, "/codegen-regression", input, headers) + reflectapi::rt::__request_impl(&self.client, "/codegen-order-coverage", input, headers) .await } + /// Coverage fixtures for codegen edge cases + #[tracing::instrument(name = "/codegen-coverage", skip(self, headers))] + pub async fn codegen_coverage( + &self, + input: super::types::myapi::coverage::CoverageRequest, + headers: reflectapi::Empty, + ) -> Result< + super::types::myapi::coverage::CoverageResponse, + reflectapi::rt::Error, + > { + reflectapi::rt::__request_impl(&self.client, "/codegen-coverage", input, headers).await + } } #[cfg(feature = "reqwest")] @@ -185,33 +197,167 @@ pub mod interface { pub mod types { pub mod myapi { + #[derive(Debug, serde::Deserialize, serde::Serialize)] + pub struct HealthCheckFail {} + + impl std::fmt::Display for HealthCheckFail { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", reflectapi::rt::error_to_string(self)) + } + } + + impl std::error::Error for HealthCheckFail {} + #[derive(Debug, serde::Serialize)] - pub struct CodegenRegressionRequest { - /// Pulls in `order::OrderInsertData` (bug 1) and Rust tuple - /// (bug 2) reachability. + pub struct OrderCoverageRequest { pub order: super::myapi::order::OrderInsertData, - /// Pulls in `order::RateLimit` for Duration (bug 3). pub rate_limit: super::myapi::order::RateLimit, - /// Pulls in `order::Policy` for PhantomData (bug 4). pub policy: super::myapi::order::Policy, } #[derive(Debug, serde::Deserialize)] - pub struct CodegenRegressionResponse { + pub struct OrderCoverageResponse { pub ok: bool, } - #[derive(Debug, serde::Deserialize, serde::Serialize)] - pub struct HealthCheckFail {} + pub mod coverage { - impl std::fmt::Display for HealthCheckFail { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", reflectapi::rt::error_to_string(self)) + #[derive(Debug, serde::Serialize)] + pub struct BaseModel { + pub label: std::string::String, } - } - impl std::error::Error for HealthCheckFail {} + #[derive(Debug, serde::Serialize)] + pub struct CoverageRequest { + pub keywords: super::super::myapi::coverage::PyKeywordFields, + pub reserved: super::super::myapi::coverage::PydanticReservedFields, + pub tree: super::super::myapi::coverage::TreeNode, + pub mutual: super::super::myapi::coverage::MutualA, + pub generic_tree: super::super::myapi::coverage::GenericTree, + pub keyword_variants: super::super::myapi::coverage::KeywordVariants, + pub int_keyed: super::super::myapi::coverage::IntKeyedMap, + pub user_id: super::super::myapi::coverage::UserId, + pub deep_option: super::super::myapi::coverage::DeepOption, + pub shadowing: super::super::myapi::coverage::ShadowingFields, + pub empty: super::super::myapi::coverage::EmptyStruct, + pub weird_doc: super::super::myapi::coverage::WeirdDocstring, + pub shadow_base_model: super::super::myapi::coverage::BaseModel, + pub wrapper_int: super::super::myapi::coverage::Wrapper, + pub wrapper_str: super::super::myapi::coverage::Wrapper, + pub defaulted: super::super::myapi::coverage::DefaultedField, + } + + #[derive(Debug, serde::Deserialize)] + pub struct CoverageResponse { + pub ok: bool, + } + + #[derive(Debug, serde::Serialize)] + pub struct DeepOption { + pub maybe_maybe: reflectapi::Option>, + } + + #[derive(Debug, serde::Serialize)] + pub struct DefaultedField { + #[serde(default = "Default::default")] + pub count: u32, + } + + #[derive(Debug, serde::Serialize)] + pub struct EmptyStruct {} + #[derive(Debug, serde::Serialize)] + pub struct GenericTree { + pub value: T, + pub children: std::vec::Vec>, + } + + #[derive(Debug, serde::Serialize)] + pub struct IntKeyedMap { + pub by_id: std::collections::HashMap, + pub uuid_keyed: std::collections::HashMap, + } + + #[derive(Debug, serde::Serialize)] + #[serde(tag = "kind")] + pub enum KeywordVariants { + #[serde(rename = "class")] + Class, + #[serde(rename = "lambda")] + Lambda { name: std::string::String }, + #[serde(rename = "return")] + Return { value: i32 }, + } + + #[derive(Debug, serde::Serialize)] + pub struct MutualA { + pub name: std::string::String, + pub b: std::option::Option>, + } + + #[derive(Debug, serde::Serialize)] + pub struct MutualB { + pub name: std::string::String, + pub a: std::option::Option>, + } + + #[derive(Debug, serde::Serialize)] + pub struct PyKeywordFields { + pub class: std::string::String, + pub from: u32, + pub import: bool, + pub lambda: i64, + #[serde(rename = "return")] + pub return_: std::vec::Vec, + #[serde(rename = "yield")] + pub yield_: std::option::Option, + #[serde(rename = "None")] + pub none: std::option::Option, + #[serde(rename = "True")] + pub true_: bool, + #[serde(rename = "type")] + pub type_: std::string::String, + #[serde(rename = "match")] + pub match_: std::string::String, + } + + #[derive(Debug, serde::Serialize)] + pub struct PydanticReservedFields { + pub model_config: std::string::String, + pub model_fields_set: std::string::String, + pub model_dump_json: std::string::String, + } + + #[derive(Debug, serde::Serialize)] + pub struct ShadowingFields { + pub field: std::string::String, + pub annotated: std::string::String, + pub generic: std::string::String, + pub base_model: std::string::String, + } + + #[derive(Debug, serde::Serialize)] + pub struct TreeNode { + pub value: std::string::String, + pub children: std::vec::Vec, + pub parent: + std::option::Option>, + } + + pub type UserId = u64; + + /// A docstring with \"quotes\" and \'apostrophes\' and a backslash: \\\\ + /// And a \"\"\"triple quote\"\"\" inside. + #[derive(Debug, serde::Serialize)] + pub struct WeirdDocstring { + pub value: std::string::String, + } + + #[derive(Debug, serde::Serialize)] + pub struct Wrapper { + pub inner: T, + } + } pub mod model { #[derive(Debug, serde::Deserialize, serde::Serialize)] @@ -308,25 +454,20 @@ pub mod types { } pub mod order { - /// Bug 1 (namespace alias mismatch). The struct name starts with - /// the parent namespace\'s cap (`Order…`) — the alias-stripping - /// pass used to strip the leading cap from the namespace alias - /// (`order.InsertData = OrderInsertData`) while field annotations - /// kept the un-stripped form, producing `order.OrderInsertData` - /// which doesn\'t resolve at `model_rebuild()` time. + /// A struct whose name begins with the parent namespace cap. + /// Exercises the namespace-alias path for both the stripped + /// (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) + /// forms. #[derive(Debug, serde::Serialize)] pub struct OrderInsertData { pub identity: std::string::String, - /// Bug 2 (tuple types). serde emits Rust tuples as JSON arrays; - /// the Python codegen used to emit `std.tuple.Tuple2[...]` - /// with no matching class, so any reference broke at rebuild. + /// Exercises the `tuple[A, B]` rendering for `(A, B)`. pub alternative_part_number: std::option::Option<(std::string::String, std::string::String)>, } - /// Bug 4 (PhantomData). PhantomData has no wire data — serde - /// skips it. The codegen used to emit `std.marker.PhantomData[T]` - /// as a field annotation, leaving a dangling reference. + /// Exercises `PhantomData` elision (the field carries no wire + /// data and must not appear in the Python model). #[derive(Debug, serde::Serialize)] pub struct Policy { pub name: std::string::String, @@ -334,10 +475,8 @@ pub mod types { pub _output_marker: std::marker::PhantomData, } - /// Bug 3 (Duration shape). serde emits `Duration` as - /// `{\"secs\": , \"nanos\": }`. Pydantic\'s `timedelta` - /// validator rejects that shape, so any response with a Duration - /// failed validation. + /// Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire + /// adapter. #[derive(Debug, serde::Serialize)] pub struct RateLimit { pub retry_after: std::time::Duration, diff --git a/reflectapi-demo/clients/typescript/generated.ts b/reflectapi-demo/clients/typescript/generated.ts index 7dc7a086..2f50da44 100644 --- a/reflectapi-demo/clients/typescript/generated.ts +++ b/reflectapi-demo/clients/typescript/generated.ts @@ -635,13 +635,21 @@ class __EventSourceParserStream extends TransformStream< export namespace __definition { export interface Interface { /** - * Regression-test endpoint pinning codegen bugs + * Coverage fixtures for namespace/tuple/Duration/PhantomData rendering */ - codegen_regression: ( - input: myapi.CodegenRegressionRequest, + codegen_order_coverage: ( + input: myapi.OrderCoverageRequest, headers: {}, options?: RequestOptions, - ) => AsyncResult; + ) => AsyncResult; + /** + * Coverage fixtures for codegen edge cases + */ + codegen_coverage: ( + input: myapi.coverage.CoverageRequest, + headers: {}, + options?: RequestOptions, + ) => AsyncResult; health: HealthInterface; pets: PetsInterface; } @@ -727,27 +735,133 @@ export namespace __definition { } } export namespace myapi { - export interface CodegenRegressionRequest { - /** - * Pulls in `order::OrderInsertData` (bug 1) and Rust tuple - * (bug 2) reachability. - */ + export interface HealthCheckFail {} + + export interface OrderCoverageRequest { order: myapi.order.OrderInsertData; - /** - * Pulls in `order::RateLimit` for Duration (bug 3). - */ rate_limit: myapi.order.RateLimit; - /** - * Pulls in `order::Policy` for PhantomData (bug 4). - */ policy: myapi.order.Policy; } - export interface CodegenRegressionResponse { + export interface OrderCoverageResponse { ok: boolean; } - export interface HealthCheckFail {} + export namespace coverage { + export interface BaseModel { + label: string; + } + + export interface CoverageRequest { + keywords: myapi.coverage.PyKeywordFields; + reserved: myapi.coverage.PydanticReservedFields; + tree: myapi.coverage.TreeNode; + mutual: myapi.coverage.MutualA; + generic_tree: myapi.coverage.GenericTree; + keyword_variants: myapi.coverage.KeywordVariants; + int_keyed: myapi.coverage.IntKeyedMap; + user_id: myapi.coverage.UserId; + deep_option: myapi.coverage.DeepOption; + shadowing: myapi.coverage.ShadowingFields; + empty: myapi.coverage.EmptyStruct; + weird_doc: myapi.coverage.WeirdDocstring; + shadow_base_model: myapi.coverage.BaseModel; + wrapper_int: myapi.coverage.Wrapper; + wrapper_str: myapi.coverage.Wrapper; + defaulted: myapi.coverage.DefaultedField; + } + + export interface CoverageResponse { + ok: boolean; + } + + export interface DeepOption { + maybe_maybe: (string | null) | null | undefined; + } + + export interface DefaultedField { + count?: number /* u32 */; + } + + export interface EmptyStruct {} + + export interface GenericTree { + value: T; + children: Array>; + } + + export interface IntKeyedMap { + by_id: Record; + uuid_keyed: Record; + } + + export type KeywordVariants = + | { kind: "class" } + | { + kind: "lambda"; + name: string; + } + | { + kind: "return"; + value: number /* i32 */; + }; + + export interface MutualA { + name: string; + b: myapi.coverage.MutualB | null; + } + + export interface MutualB { + name: string; + a: myapi.coverage.MutualA | null; + } + + export interface PyKeywordFields { + class: string; + from: number /* u32 */; + import: boolean; + lambda: number /* i64 */; + return: Array; + yield: string | null; + None: number /* i32 */ | null; + True: boolean; + type: string; + match: string; + } + + export interface PydanticReservedFields { + model_config: string; + model_fields_set: string; + model_dump_json: string; + } + + export interface ShadowingFields { + field: string; + annotated: string; + generic: string; + base_model: string; + } + + export interface TreeNode { + value: string; + children: Array; + parent: myapi.coverage.TreeNode | null; + } + + export type UserId = number /* u64 */; + + /** + * A docstring with \"quotes\" and \'apostrophes\' and a backslash: \\\\ + * And a \"\"\"triple quote\"\"\" inside. + */ + export interface WeirdDocstring { + value: string; + } + + export interface Wrapper { + inner: T; + } + } export namespace model { export type Behavior = @@ -853,27 +967,22 @@ export namespace myapi { export namespace order { /** - * Bug 1 (namespace alias mismatch). The struct name starts with - * the parent namespace\'s cap (`Order…`) — the alias-stripping - * pass used to strip the leading cap from the namespace alias - * (`order.InsertData = OrderInsertData`) while field annotations - * kept the un-stripped form, producing `order.OrderInsertData` - * which doesn\'t resolve at `model_rebuild()` time. + * A struct whose name begins with the parent namespace cap. + * Exercises the namespace-alias path for both the stripped + * (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) + * forms. */ export interface OrderInsertData { identity: string; /** - * Bug 2 (tuple types). serde emits Rust tuples as JSON arrays; - * the Python codegen used to emit `std.tuple.Tuple2[...]` - * with no matching class, so any reference broke at rebuild. + * Exercises the `tuple[A, B]` rendering for `(A, B)`. */ alternative_part_number: [string, string] | null; } /** - * Bug 4 (PhantomData). PhantomData has no wire data — serde - * skips it. The codegen used to emit `std.marker.PhantomData[T]` - * as a field annotation, leaving a dangling reference. + * Exercises `PhantomData` elision (the field carries no wire + * data and must not appear in the Python model). */ export interface Policy { name: string; @@ -882,10 +991,8 @@ export namespace myapi { } /** - * Bug 3 (Duration shape). serde emits `Duration` as - * `{\"secs\": , \"nanos\": }`. Pydantic\'s `timedelta` - * validator rejects that shape, so any response with a Duration - * failed validation. + * Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire + * adapter. */ export interface RateLimit { retry_after: std.time.Duration; @@ -1016,7 +1123,8 @@ namespace __implementation { typeof base === "string" ? new ClientInstance(base) : base; return { impl: { - codegen_regression: codegen_regression(client_instance), + codegen_order_coverage: codegen_order_coverage(client_instance), + codegen_coverage: codegen_coverage(client_instance), health: { check: health__check(client_instance), }, @@ -1136,17 +1244,30 @@ namespace __implementation { myapi.proto.UnauthorizedError >(client, "/pets.cdc-events", input, headers, options); } - function codegen_regression(client: Client) { + function codegen_order_coverage(client: Client) { + return ( + input: myapi.OrderCoverageRequest, + headers: {}, + options?: RequestOptions, + ) => + __request< + myapi.OrderCoverageRequest, + {}, + myapi.OrderCoverageResponse, + {} + >(client, "/codegen-order-coverage", input, headers, options); + } + function codegen_coverage(client: Client) { return ( - input: myapi.CodegenRegressionRequest, + input: myapi.coverage.CoverageRequest, headers: {}, options?: RequestOptions, ) => __request< - myapi.CodegenRegressionRequest, + myapi.coverage.CoverageRequest, {}, - myapi.CodegenRegressionResponse, + myapi.coverage.CoverageResponse, {} - >(client, "/codegen-regression", input, headers, options); + >(client, "/codegen-coverage", input, headers, options); } } diff --git a/reflectapi-demo/openapi.json b/reflectapi-demo/openapi.json index 0a4b3568..7ffa715e 100644 --- a/reflectapi-demo/openapi.json +++ b/reflectapi-demo/openapi.json @@ -6,16 +6,16 @@ "version": "1.0.0" }, "paths": { - "/codegen-regression": { - "description": "Regression-test endpoint pinning codegen bugs", + "/codegen-coverage": { + "description": "Coverage fixtures for codegen edge cases", "post": { - "operationId": "codegen-regression", - "description": "Regression-test endpoint pinning codegen bugs", + "operationId": "codegen-coverage", + "description": "Coverage fixtures for codegen edge cases", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/myapi.CodegenRegressionRequest" + "$ref": "#/components/schemas/myapi.coverage.CoverageRequest" } } }, @@ -27,7 +27,36 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/myapi.CodegenRegressionResponse" + "$ref": "#/components/schemas/myapi.coverage.CoverageResponse" + } + } + } + } + } + } + }, + "/codegen-order-coverage": { + "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", + "post": { + "operationId": "codegen-order-coverage", + "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.OrderCoverageRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.OrderCoverageResponse" } } } @@ -418,9 +447,17 @@ "description": "64-bit floating point number", "type": "number" }, - "myapi.CodegenRegressionRequest": { + "i32": { + "description": "32-bit signed integer", + "type": "integer" + }, + "i64": { + "description": "64-bit signed integer", + "type": "integer" + }, + "myapi.OrderCoverageRequest": { "type": "object", - "title": "myapi.CodegenRegressionRequest", + "title": "myapi.OrderCoverageRequest", "required": [ "order", "policy", @@ -428,11 +465,10 @@ ], "properties": { "order": { - "description": "Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability.", "$ref": "#/components/schemas/myapi.order.OrderInsertData" }, "policy": { - "description": "Pulls in `order::Policy` for PhantomData (bug 4).", + "description": "Exercises `PhantomData` elision (the field carries no wire\ndata and must not appear in the Python model).", "type": "object", "title": "myapi.order.Policy", "required": [ @@ -455,14 +491,144 @@ } }, "rate_limit": { - "description": "Pulls in `order::RateLimit` for Duration (bug 3).", "$ref": "#/components/schemas/myapi.order.RateLimit" } } }, - "myapi.CodegenRegressionResponse": { + "myapi.OrderCoverageResponse": { + "type": "object", + "title": "myapi.OrderCoverageResponse", + "required": [ + "ok" + ], + "properties": { + "ok": { + "$ref": "#/components/schemas/bool" + } + } + }, + "myapi.coverage.BaseModel": { + "type": "object", + "title": "myapi.coverage.BaseModel", + "required": [ + "label" + ], + "properties": { + "label": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.CoverageRequest": { + "type": "object", + "title": "myapi.coverage.CoverageRequest", + "required": [ + "deep_option", + "defaulted", + "empty", + "generic_tree", + "int_keyed", + "keyword_variants", + "keywords", + "mutual", + "reserved", + "shadow_base_model", + "shadowing", + "tree", + "user_id", + "weird_doc", + "wrapper_int", + "wrapper_str" + ], + "properties": { + "deep_option": { + "$ref": "#/components/schemas/myapi.coverage.DeepOption" + }, + "defaulted": { + "$ref": "#/components/schemas/myapi.coverage.DefaultedField" + }, + "empty": { + "$ref": "#/components/schemas/myapi.coverage.EmptyStruct" + }, + "generic_tree": { + "type": "object", + "title": "myapi.coverage.GenericTree", + "required": [ + "children", + "value" + ], + "properties": { + "children": { + "description": "Expandable array type", + "type": "array", + "items": { + "$ref": "#/components/schemas/myapi.coverage.GenericTree_i32_" + } + }, + "value": { + "$ref": "#/components/schemas/i32" + } + } + }, + "int_keyed": { + "$ref": "#/components/schemas/myapi.coverage.IntKeyedMap" + }, + "keyword_variants": { + "$ref": "#/components/schemas/myapi.coverage.KeywordVariants" + }, + "keywords": { + "$ref": "#/components/schemas/myapi.coverage.PyKeywordFields" + }, + "mutual": { + "$ref": "#/components/schemas/myapi.coverage.MutualA" + }, + "reserved": { + "$ref": "#/components/schemas/myapi.coverage.PydanticReservedFields" + }, + "shadow_base_model": { + "$ref": "#/components/schemas/myapi.coverage.BaseModel" + }, + "shadowing": { + "$ref": "#/components/schemas/myapi.coverage.ShadowingFields" + }, + "tree": { + "$ref": "#/components/schemas/myapi.coverage.TreeNode" + }, + "user_id": { + "$ref": "#/components/schemas/u64" + }, + "weird_doc": { + "$ref": "#/components/schemas/myapi.coverage.WeirdDocstring" + }, + "wrapper_int": { + "type": "object", + "title": "myapi.coverage.Wrapper", + "required": [ + "inner" + ], + "properties": { + "inner": { + "$ref": "#/components/schemas/i32" + } + } + }, + "wrapper_str": { + "type": "object", + "title": "myapi.coverage.Wrapper", + "required": [ + "inner" + ], + "properties": { + "inner": { + "$ref": "#/components/schemas/std.string.String" + } + } + } + } + }, + "myapi.coverage.CoverageResponse": { "type": "object", - "title": "myapi.CodegenRegressionResponse", + "title": "myapi.coverage.CoverageResponse", "required": [ "ok" ], @@ -472,6 +638,325 @@ } } }, + "myapi.coverage.DeepOption": { + "type": "object", + "title": "myapi.coverage.DeepOption", + "required": [ + "maybe_maybe" + ], + "properties": { + "maybe_maybe": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/std.string.String" + } + ] + } + ] + } + } + }, + "myapi.coverage.DefaultedField": { + "type": "object", + "title": "myapi.coverage.DefaultedField", + "properties": { + "count": { + "$ref": "#/components/schemas/u32" + } + } + }, + "myapi.coverage.EmptyStruct": { + "type": "object", + "title": "myapi.coverage.EmptyStruct", + "properties": {} + }, + "myapi.coverage.IntKeyedMap": { + "type": "object", + "title": "myapi.coverage.IntKeyedMap", + "required": [ + "by_id", + "uuid_keyed" + ], + "properties": { + "by_id": { + "description": "Key-value map type", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/std.string.String" + } + }, + "uuid_keyed": { + "description": "Key-value map type", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/std.string.String" + } + } + } + }, + "myapi.coverage.KeywordVariants": { + "oneOf": [ + { + "type": "object", + "title": "class", + "required": [ + "kind" + ], + "properties": { + "kind": { + "const": "class" + } + } + }, + { + "type": "object", + "title": "lambda", + "required": [ + "kind", + "name" + ], + "properties": { + "kind": { + "const": "lambda" + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + { + "type": "object", + "title": "return", + "required": [ + "kind", + "value" + ], + "properties": { + "kind": { + "const": "return" + }, + "value": { + "$ref": "#/components/schemas/i32" + } + } + } + ] + }, + "myapi.coverage.MutualA": { + "type": "object", + "title": "myapi.coverage.MutualA", + "required": [ + "b", + "name" + ], + "properties": { + "b": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/myapi.coverage.MutualB" + } + ] + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.MutualB": { + "type": "object", + "title": "myapi.coverage.MutualB", + "required": [ + "a", + "name" + ], + "properties": { + "a": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/myapi.coverage.MutualA" + } + ] + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.PyKeywordFields": { + "type": "object", + "title": "myapi.coverage.PyKeywordFields", + "required": [ + "None", + "True", + "class", + "from", + "import", + "lambda", + "match", + "return", + "type", + "yield" + ], + "properties": { + "None": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/i32" + } + ] + }, + "True": { + "$ref": "#/components/schemas/bool" + }, + "class": { + "$ref": "#/components/schemas/std.string.String" + }, + "from": { + "$ref": "#/components/schemas/u32" + }, + "import": { + "$ref": "#/components/schemas/bool" + }, + "lambda": { + "$ref": "#/components/schemas/i64" + }, + "match": { + "$ref": "#/components/schemas/std.string.String" + }, + "return": { + "description": "Expandable array type", + "type": "array", + "items": { + "$ref": "#/components/schemas/u8" + } + }, + "type": { + "$ref": "#/components/schemas/std.string.String" + }, + "yield": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/std.string.String" + } + ] + } + } + }, + "myapi.coverage.PydanticReservedFields": { + "type": "object", + "title": "myapi.coverage.PydanticReservedFields", + "required": [ + "model_config", + "model_dump_json", + "model_fields_set" + ], + "properties": { + "model_config": { + "$ref": "#/components/schemas/std.string.String" + }, + "model_dump_json": { + "$ref": "#/components/schemas/std.string.String" + }, + "model_fields_set": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.ShadowingFields": { + "type": "object", + "title": "myapi.coverage.ShadowingFields", + "required": [ + "annotated", + "base_model", + "field", + "generic" + ], + "properties": { + "annotated": { + "$ref": "#/components/schemas/std.string.String" + }, + "base_model": { + "$ref": "#/components/schemas/std.string.String" + }, + "field": { + "$ref": "#/components/schemas/std.string.String" + }, + "generic": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.TreeNode": { + "type": "object", + "title": "myapi.coverage.TreeNode", + "required": [ + "children", + "parent", + "value" + ], + "properties": { + "children": { + "description": "Expandable array type", + "type": "array", + "items": { + "$ref": "#/components/schemas/myapi.coverage.TreeNode" + } + }, + "parent": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/myapi.coverage.TreeNode" + } + ] + }, + "value": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.WeirdDocstring": { + "description": "A docstring with \\\"quotes\\\" and \\'apostrophes\\' and a backslash: \\\\\\\\\nAnd a \\\"\\\"\\\"triple quote\\\"\\\"\\\" inside.", + "type": "object", + "title": "myapi.coverage.WeirdDocstring", + "required": [ + "value" + ], + "properties": { + "value": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, "myapi.model.Behavior": { "oneOf": [ { @@ -665,7 +1150,7 @@ } }, "myapi.order.OrderInsertData": { - "description": "Bug 1 (namespace alias mismatch). The struct name starts with\nthe parent namespace\\'s cap (`Order…`) — the alias-stripping\npass used to strip the leading cap from the namespace alias\n(`order.InsertData = OrderInsertData`) while field annotations\nkept the un-stripped form, producing `order.OrderInsertData`\nwhich doesn\\'t resolve at `model_rebuild()` time.", + "description": "A struct whose name begins with the parent namespace cap.\nExercises the namespace-alias path for both the stripped\n(`order.InsertData`) and Rust-leaf (`order.OrderInsertData`)\nforms.", "type": "object", "title": "myapi.order.OrderInsertData", "required": [ @@ -674,7 +1159,7 @@ ], "properties": { "alternative_part_number": { - "description": "Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + "description": "Exercises the `tuple[A, B]` rendering for `(A, B)`.", "oneOf": [ { "description": "Null", @@ -700,7 +1185,7 @@ } }, "myapi.order.RateLimit": { - "description": "Bug 3 (Duration shape). serde emits `Duration` as\n`{\\\"secs\\\": , \\\"nanos\\\": }`. Pydantic\\'s `timedelta`\nvalidator rejects that shape, so any response with a Duration\nfailed validation.", + "description": "Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire\nadapter.", "type": "object", "title": "myapi.order.RateLimit", "required": [ diff --git a/reflectapi-demo/reflectapi.json b/reflectapi-demo/reflectapi.json index 39542941..5200cea0 100644 --- a/reflectapi-demo/reflectapi.json +++ b/reflectapi-demo/reflectapi.json @@ -169,15 +169,31 @@ "readonly": true }, { - "name": "codegen-regression", + "name": "codegen-order-coverage", "path": "", - "description": "Regression-test endpoint pinning codegen bugs", + "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", "input_type": { - "name": "myapi::CodegenRegressionRequest" + "name": "myapi::OrderCoverageRequest" }, "output_kind": "complete", "output_type": { - "name": "myapi::CodegenRegressionResponse" + "name": "myapi::OrderCoverageResponse" + }, + "serialization": [ + "json", + "msgpack" + ] + }, + { + "name": "codegen-coverage", + "path": "", + "description": "Coverage fixtures for codegen edge cases", + "input_type": { + "name": "myapi::coverage::CoverageRequest" + }, + "output_kind": "complete", + "output_type": { + "name": "myapi::coverage::CoverageResponse" }, "serialization": [ "json", @@ -188,55 +204,647 @@ "input_types": { "types": [ { - "kind": "primitive", - "name": "chrono::DateTime", - "description": "DateTime at a given timezone (RFC3339 format)", - "parameters": [ - { - "name": "Tz" - } - ], - "fallback": { - "name": "std::string::String" + "kind": "primitive", + "name": "bool", + "description": "Boolean value" + }, + { + "kind": "primitive", + "name": "chrono::DateTime", + "description": "DateTime at a given timezone (RFC3339 format)", + "parameters": [ + { + "name": "Tz" + } + ], + "fallback": { + "name": "std::string::String" + } + }, + { + "kind": "primitive", + "name": "f64", + "description": "64-bit floating point number" + }, + { + "kind": "primitive", + "name": "i32", + "description": "32-bit signed integer" + }, + { + "kind": "primitive", + "name": "i64", + "description": "64-bit signed integer" + }, + { + "kind": "struct", + "name": "myapi::OrderCoverageRequest", + "fields": { + "named": [ + { + "name": "order", + "type": { + "name": "myapi::order::OrderInsertData" + }, + "required": true + }, + { + "name": "rate_limit", + "type": { + "name": "myapi::order::RateLimit" + }, + "required": true + }, + { + "name": "policy", + "type": { + "name": "myapi::order::Policy", + "arguments": [ + { + "name": "std::string::String" + }, + { + "name": "u32" + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::BaseModel", + "fields": { + "named": [ + { + "name": "label", + "type": { + "name": "std::string::String" + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::CoverageRequest", + "fields": { + "named": [ + { + "name": "keywords", + "type": { + "name": "myapi::coverage::PyKeywordFields" + }, + "required": true + }, + { + "name": "reserved", + "type": { + "name": "myapi::coverage::PydanticReservedFields" + }, + "required": true + }, + { + "name": "tree", + "type": { + "name": "myapi::coverage::TreeNode" + }, + "required": true + }, + { + "name": "mutual", + "type": { + "name": "myapi::coverage::MutualA" + }, + "required": true + }, + { + "name": "generic_tree", + "type": { + "name": "myapi::coverage::GenericTree", + "arguments": [ + { + "name": "i32" + } + ] + }, + "required": true + }, + { + "name": "keyword_variants", + "type": { + "name": "myapi::coverage::KeywordVariants" + }, + "required": true + }, + { + "name": "int_keyed", + "type": { + "name": "myapi::coverage::IntKeyedMap" + }, + "required": true + }, + { + "name": "user_id", + "type": { + "name": "myapi::coverage::UserId" + }, + "required": true + }, + { + "name": "deep_option", + "type": { + "name": "myapi::coverage::DeepOption" + }, + "required": true + }, + { + "name": "shadowing", + "type": { + "name": "myapi::coverage::ShadowingFields" + }, + "required": true + }, + { + "name": "empty", + "type": { + "name": "myapi::coverage::EmptyStruct" + }, + "required": true + }, + { + "name": "weird_doc", + "type": { + "name": "myapi::coverage::WeirdDocstring" + }, + "required": true + }, + { + "name": "shadow_base_model", + "type": { + "name": "myapi::coverage::BaseModel" + }, + "required": true + }, + { + "name": "wrapper_int", + "type": { + "name": "myapi::coverage::Wrapper", + "arguments": [ + { + "name": "i32" + } + ] + }, + "required": true + }, + { + "name": "wrapper_str", + "type": { + "name": "myapi::coverage::Wrapper", + "arguments": [ + { + "name": "std::string::String" + } + ] + }, + "required": true + }, + { + "name": "defaulted", + "type": { + "name": "myapi::coverage::DefaultedField" + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::DeepOption", + "fields": { + "named": [ + { + "name": "maybe_maybe", + "type": { + "name": "reflectapi::Option", + "arguments": [ + { + "name": "std::option::Option", + "arguments": [ + { + "name": "std::string::String" + } + ] + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::DefaultedField", + "fields": { + "named": [ + { + "name": "count", + "type": { + "name": "u32" + } + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::EmptyStruct", + "fields": { + "named": [] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::GenericTree", + "parameters": [ + { + "name": "T" + } + ], + "fields": { + "named": [ + { + "name": "value", + "type": { + "name": "T" + }, + "required": true + }, + { + "name": "children", + "type": { + "name": "std::vec::Vec", + "arguments": [ + { + "name": "myapi::coverage::GenericTree", + "arguments": [ + { + "name": "T" + } + ] + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::IntKeyedMap", + "fields": { + "named": [ + { + "name": "by_id", + "type": { + "name": "std::collections::HashMap", + "arguments": [ + { + "name": "u64" + }, + { + "name": "std::string::String" + } + ] + }, + "required": true + }, + { + "name": "uuid_keyed", + "type": { + "name": "std::collections::HashMap", + "arguments": [ + { + "name": "std::string::String" + }, + { + "name": "std::string::String" + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "enum", + "name": "myapi::coverage::KeywordVariants", + "representation": { + "internal": { + "tag": "kind" + } + }, + "variants": [ + { + "name": "class", + "fields": "none" + }, + { + "name": "lambda", + "fields": { + "named": [ + { + "name": "name", + "type": { + "name": "std::string::String" + }, + "required": true + } + ] + } + }, + { + "name": "return", + "fields": { + "named": [ + { + "name": "value", + "type": { + "name": "i32" + }, + "required": true + } + ] + } + } + ] + }, + { + "kind": "struct", + "name": "myapi::coverage::MutualA", + "fields": { + "named": [ + { + "name": "name", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "b", + "type": { + "name": "std::option::Option", + "arguments": [ + { + "name": "std::boxed::Box", + "arguments": [ + { + "name": "myapi::coverage::MutualB" + } + ] + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::MutualB", + "fields": { + "named": [ + { + "name": "name", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "a", + "type": { + "name": "std::option::Option", + "arguments": [ + { + "name": "std::boxed::Box", + "arguments": [ + { + "name": "myapi::coverage::MutualA" + } + ] + } + ] + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::PyKeywordFields", + "fields": { + "named": [ + { + "name": "class", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "from", + "type": { + "name": "u32" + }, + "required": true + }, + { + "name": "import", + "type": { + "name": "bool" + }, + "required": true + }, + { + "name": "lambda", + "type": { + "name": "i64" + }, + "required": true + }, + { + "name": "return", + "type": { + "name": "std::vec::Vec", + "arguments": [ + { + "name": "u8" + } + ] + }, + "required": true + }, + { + "name": "yield", + "type": { + "name": "std::option::Option", + "arguments": [ + { + "name": "std::string::String" + } + ] + }, + "required": true + }, + { + "name": "None", + "type": { + "name": "std::option::Option", + "arguments": [ + { + "name": "i32" + } + ] + }, + "required": true + }, + { + "name": "True", + "type": { + "name": "bool" + }, + "required": true + }, + { + "name": "type", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "match", + "type": { + "name": "std::string::String" + }, + "required": true + } + ] } }, { - "kind": "primitive", - "name": "f64", - "description": "64-bit floating point number" + "kind": "struct", + "name": "myapi::coverage::PydanticReservedFields", + "fields": { + "named": [ + { + "name": "model_config", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "model_fields_set", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "model_dump_json", + "type": { + "name": "std::string::String" + }, + "required": true + } + ] + } }, { "kind": "struct", - "name": "myapi::CodegenRegressionRequest", + "name": "myapi::coverage::ShadowingFields", "fields": { "named": [ { - "name": "order", - "description": "Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability.", + "name": "field", "type": { - "name": "myapi::order::OrderInsertData" + "name": "std::string::String" }, "required": true }, { - "name": "rate_limit", - "description": "Pulls in `order::RateLimit` for Duration (bug 3).", + "name": "annotated", "type": { - "name": "myapi::order::RateLimit" + "name": "std::string::String" }, "required": true }, { - "name": "policy", - "description": "Pulls in `order::Policy` for PhantomData (bug 4).", + "name": "generic", "type": { - "name": "myapi::order::Policy", + "name": "std::string::String" + }, + "required": true + }, + { + "name": "base_model", + "type": { + "name": "std::string::String" + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::TreeNode", + "fields": { + "named": [ + { + "name": "value", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "children", + "type": { + "name": "std::vec::Vec", "arguments": [ { - "name": "std::string::String" - }, + "name": "myapi::coverage::TreeNode" + } + ] + }, + "required": true + }, + { + "name": "parent", + "type": { + "name": "std::option::Option", + "arguments": [ { - "name": "u32" + "name": "std::boxed::Box", + "arguments": [ + { + "name": "myapi::coverage::TreeNode" + } + ] } ] }, @@ -245,6 +853,58 @@ ] } }, + { + "kind": "struct", + "name": "myapi::coverage::UserId", + "fields": { + "unnamed": [ + { + "name": "0", + "type": { + "name": "u64" + }, + "required": true + } + ] + }, + "transparent": true + }, + { + "kind": "struct", + "name": "myapi::coverage::WeirdDocstring", + "description": "A docstring with \\\"quotes\\\" and \\'apostrophes\\' and a backslash: \\\\\\\\\nAnd a \\\"\\\"\\\"triple quote\\\"\\\"\\\" inside.", + "fields": { + "named": [ + { + "name": "value", + "type": { + "name": "std::string::String" + }, + "required": true + } + ] + } + }, + { + "kind": "struct", + "name": "myapi::coverage::Wrapper", + "parameters": [ + { + "name": "T" + } + ], + "fields": { + "named": [ + { + "name": "inner", + "type": { + "name": "T" + }, + "required": true + } + ] + } + }, { "kind": "enum", "name": "myapi::model::Behavior", @@ -411,7 +1071,7 @@ { "kind": "struct", "name": "myapi::order::OrderInsertData", - "description": "Bug 1 (namespace alias mismatch). The struct name starts with\nthe parent namespace\\'s cap (`Order…`) — the alias-stripping\npass used to strip the leading cap from the namespace alias\n(`order.InsertData = OrderInsertData`) while field annotations\nkept the un-stripped form, producing `order.OrderInsertData`\nwhich doesn\\'t resolve at `model_rebuild()` time.", + "description": "A struct whose name begins with the parent namespace cap.\nExercises the namespace-alias path for both the stripped\n(`order.InsertData`) and Rust-leaf (`order.OrderInsertData`)\nforms.", "fields": { "named": [ { @@ -423,7 +1083,7 @@ }, { "name": "alternative_part_number", - "description": "Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + "description": "Exercises the `tuple[A, B]` rendering for `(A, B)`.", "type": { "name": "std::option::Option", "arguments": [ @@ -448,7 +1108,7 @@ { "kind": "struct", "name": "myapi::order::Policy", - "description": "Bug 4 (PhantomData). PhantomData has no wire data — serde\nskips it. The codegen used to emit `std.marker.PhantomData[T]`\nas a field annotation, leaving a dangling reference.", + "description": "Exercises `PhantomData` elision (the field carries no wire\ndata and must not appear in the Python model).", "parameters": [ { "name": "C" @@ -496,7 +1156,7 @@ { "kind": "struct", "name": "myapi::order::RateLimit", - "description": "Bug 3 (Duration shape). serde emits `Duration` as\n`{\\\"secs\\\": , \\\"nanos\\\": }`. Pydantic\\'s `timedelta`\nvalidator rejects that shape, so any response with a Duration\nfailed validation.", + "description": "Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire\nadapter.", "fields": { "named": [ { @@ -698,6 +1358,32 @@ } ] }, + { + "kind": "primitive", + "name": "std::boxed::Box", + "description": "std::boxed::Box pointer type", + "parameters": [ + { + "name": "T" + } + ], + "fallback": { + "name": "T" + } + }, + { + "kind": "primitive", + "name": "std::collections::HashMap", + "description": "Key-value map type", + "parameters": [ + { + "name": "K" + }, + { + "name": "V" + } + ] + }, { "kind": "primitive", "name": "std::marker::PhantomData", @@ -835,7 +1521,14 @@ }, { "kind": "struct", - "name": "myapi::CodegenRegressionResponse", + "name": "myapi::HealthCheckFail", + "fields": { + "named": [] + } + }, + { + "kind": "struct", + "name": "myapi::OrderCoverageResponse", "fields": { "named": [ { @@ -850,9 +1543,17 @@ }, { "kind": "struct", - "name": "myapi::HealthCheckFail", + "name": "myapi::coverage::CoverageResponse", "fields": { - "named": [] + "named": [ + { + "name": "ok", + "type": { + "name": "bool" + }, + "required": true + } + ] } }, { diff --git a/reflectapi-demo/src/lib.rs b/reflectapi-demo/src/lib.rs index 58f589f0..228b8030 100644 --- a/reflectapi-demo/src/lib.rs +++ b/reflectapi-demo/src/lib.rs @@ -49,9 +49,13 @@ pub fn builder() -> reflectapi::Builder> { .readonly(true) .description("Stream of change data capture events for pets") }) - .route(codegen_regression, |b| { - b.name("codegen-regression") - .description("Regression-test endpoint pinning codegen bugs") + .route(order_coverage, |b| { + b.name("codegen-order-coverage") + .description("Coverage fixtures for namespace/tuple/Duration/PhantomData rendering") + }) + .route(codegen_coverage, |b| { + b.name("codegen-coverage") + .description("Coverage fixtures for codegen edge cases") }) .rename_types("reflectapi_demo::", "myapi::") // and some optional linting rules @@ -88,31 +92,37 @@ async fn health_check( } #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] -pub struct CodegenRegressionRequest { - /// Pulls in `order::OrderInsertData` (bug 1) and Rust tuple - /// (bug 2) reachability. +pub struct OrderCoverageRequest { pub order: order::OrderInsertData, - /// Pulls in `order::RateLimit` for Duration (bug 3). pub rate_limit: order::RateLimit, - /// Pulls in `order::Policy` for PhantomData (bug 4). pub policy: order::Policy, } #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] -pub struct CodegenRegressionResponse { +pub struct OrderCoverageResponse { pub ok: bool, } -/// Pure regression-test endpoint. Doesn't run in the demo server — it -/// only exists so the four bug-pinning types appear in the generated -/// schema and the Python codegen has to render them. -async fn codegen_regression( +/// Endpoint that drags the `order::` coverage fixtures into the +/// schema so the smoke test exercises their rendering. Not intended +/// to be called from the demo server. +async fn order_coverage( + _: Arc, + request: OrderCoverageRequest, + _headers: reflectapi::Empty, +) -> Result { + let _ = request; + Ok(OrderCoverageResponse { ok: true }) +} + +/// Endpoint that drags the `coverage::` fixtures into the schema. +async fn codegen_coverage( _: Arc, - request: CodegenRegressionRequest, + request: coverage::CoverageRequest, _headers: reflectapi::Empty, -) -> Result { +) -> Result { let _ = request; - Ok(CodegenRegressionResponse { ok: true }) + Ok(coverage::CoverageResponse { ok: true }) } #[derive(Debug)] @@ -130,34 +140,26 @@ impl Default for AppState { } } -// Regression-test module: every type here exists to exercise a -// previously-broken Python codegen path. Each comment block links the -// type to the bug it pins. +// Coverage fixtures exercised by the codegen smoke tests. mod order { use std::marker::PhantomData; use std::time::Duration; - /// Bug 1 (namespace alias mismatch). The struct name starts with - /// the parent namespace's cap (`Order…`) — the alias-stripping - /// pass used to strip the leading cap from the namespace alias - /// (`order.InsertData = OrderInsertData`) while field annotations - /// kept the un-stripped form, producing `order.OrderInsertData` - /// which doesn't resolve at `model_rebuild()` time. + /// A struct whose name begins with the parent namespace cap. + /// Exercises the namespace-alias path for both the stripped + /// (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) + /// forms. #[derive( Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, )] pub struct OrderInsertData { pub identity: String, - /// Bug 2 (tuple types). serde emits Rust tuples as JSON arrays; - /// the Python codegen used to emit `std.tuple.Tuple2[...]` - /// with no matching class, so any reference broke at rebuild. + /// Exercises the `tuple[A, B]` rendering for `(A, B)`. pub alternative_part_number: Option<(String, String)>, } - /// Bug 3 (Duration shape). serde emits `Duration` as - /// `{"secs": , "nanos": }`. Pydantic's `timedelta` - /// validator rejects that shape, so any response with a Duration - /// failed validation. + /// Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire + /// adapter. #[derive( Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, )] @@ -166,9 +168,8 @@ mod order { pub max_wait: Option, } - /// Bug 4 (PhantomData). PhantomData has no wire data — serde - /// skips it. The codegen used to emit `std.marker.PhantomData[T]` - /// as a field annotation, leaving a dangling reference. + /// Exercises `PhantomData` elision (the field carries no wire + /// data and must not appear in the Python model). #[derive( Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, )] @@ -178,15 +179,215 @@ mod order { T: 'static, { pub name: String, - // No `#[serde(skip)]` here — serde emits PhantomData as JSON - // `null`, and reflectapi-derive surfaces the field in the - // schema. The Python codegen used to render it as - // `std.marker.PhantomData[T]`, which broke `model_rebuild()`. pub _context_marker: PhantomData, pub _output_marker: PhantomData, } } +/// Coverage fixtures for codegen edge cases. Each type exercises a +/// specific rendering rule; the CI smoke test (regenerate + +/// `import api_client`) confirms every shape round-trips through +/// JSON cleanly. +mod coverage { + use std::collections::HashMap; + + // ---- Python-keyword and builtin field names ---- + // serde serialises these names verbatim; the codegen must produce + // a safe Python identifier and carry the wire name as a Field + // alias so the round-trip is preserved. + + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct PyKeywordFields { + #[serde(rename = "class")] + pub class_: String, + #[serde(rename = "from")] + pub from_: u32, + #[serde(rename = "import")] + pub import_: bool, + #[serde(rename = "lambda")] + pub lambda_: i64, + #[serde(rename = "return")] + pub return_: Vec, + #[serde(rename = "yield")] + pub yield_: Option, + #[serde(rename = "None")] + pub none_: Option, + #[serde(rename = "True")] + pub true_: bool, + // Python builtins that aren't keywords but shadow ergonomics. + pub r#type: String, + pub r#match: String, + } + + // ---- Pydantic-reserved field names ---- + // These names collide with `BaseModel`'s own attributes/methods on + // the Python side; the generated class must rename and alias them. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct PydanticReservedFields { + #[serde(rename = "model_config")] + pub model_config_: String, + #[serde(rename = "model_fields_set")] + pub model_fields_set_: String, + #[serde(rename = "model_dump_json")] + pub model_dump_json_: String, + } + + // ---- Self-referential type ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct TreeNode { + pub value: String, + pub children: Vec, + pub parent: Option>, + } + + // ---- Mutually recursive types ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct MutualA { + pub name: String, + pub b: Option>, + } + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct MutualB { + pub name: String, + pub a: Option>, + } + + // ---- Generic recursive type ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct GenericTree { + pub value: T, + pub children: Vec>, + } + + // ---- Enum with variants whose names are Python keywords ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + #[serde(tag = "kind", rename_all = "snake_case")] + pub enum KeywordVariants { + Class, + Lambda { name: String }, + Return { value: i32 }, + } + + // ---- HashMap with non-string keys (JSON stringifies them) ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct IntKeyedMap { + pub by_id: HashMap, + pub uuid_keyed: HashMap, + } + + // ---- Transparent newtype (no Python class emitted) ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + #[serde(transparent)] + pub struct UserId(pub u64); + + // ---- Three-state Option wrapping a regular Option ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct DeepOption { + pub maybe_maybe: reflectapi::Option>, + } + + // ---- Field names shared with symbols imported into the + // generated module (`BaseModel`, `Field`, `Annotated`, etc.) ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct ShadowingFields { + pub field: String, + pub annotated: String, + pub generic: String, + pub base_model: String, + } + + // ---- Empty struct ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct EmptyStruct {} + + // ---- Docstring with characters that need escaping ---- + /// A docstring with "quotes" and 'apostrophes' and a backslash: \\ + /// And a """triple quote""" inside. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct WeirdDocstring { + pub value: String, + } + + // ---- Type name that shadows a Pydantic-imported symbol ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct BaseModel { + pub label: String, + } + + // ---- Generic used with multiple concrete arguments ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct Wrapper { + pub inner: T, + } + + // ---- Field with a non-None serde default ---- + fn default_count() -> u32 { + 42 + } + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct DefaultedField { + #[serde(default = "default_count")] + pub count: u32, + } + + #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] + pub struct CoverageRequest { + pub keywords: PyKeywordFields, + pub reserved: PydanticReservedFields, + pub tree: TreeNode, + pub mutual: MutualA, + pub generic_tree: GenericTree, + pub keyword_variants: KeywordVariants, + pub int_keyed: IntKeyedMap, + pub user_id: UserId, + pub deep_option: DeepOption, + pub shadowing: ShadowingFields, + pub empty: EmptyStruct, + pub weird_doc: WeirdDocstring, + pub shadow_base_model: BaseModel, + pub wrapper_int: Wrapper, + pub wrapper_str: Wrapper, + pub defaulted: DefaultedField, + } + + #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] + pub struct CoverageResponse { + pub ok: bool, + } +} + mod model { #[derive( Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap index e4555685..bf6d0d49 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_deprecated-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicStructWithDeprecatedField(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") g: int = Field(serialization_alias="_g", validation_alias="_g") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap index 1d9c75ab..e8185775 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_enum_documented-4.snap @@ -45,7 +45,9 @@ T = TypeVar("T") class ReflectapiDemoTestsBasicTestEnumDocumentedVariant1Variant(BaseModel, Generic[T]): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: T = Field(description="variant1 field docs") @@ -53,7 +55,9 @@ class ReflectapiDemoTestsBasicTestEnumDocumentedVariant1Variant(BaseModel, Gener class ReflectapiDemoTestsBasicTestEnumDocumentedVariant2Variant(BaseModel, Generic[T]): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) named_field: T = Field(description="named field variant2 field docs") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap index d5d54637..d8fbea54 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_documented-4.snap @@ -29,7 +29,9 @@ class ReflectapiDemoTestsBasicTestStructDocumented(BaseModel): more more""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(description="field docs\nmultiline") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap index 63ddd590..9b89aaf3 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_empty-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructEmpty(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap index c40e67c6..f03738df 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_static_str-4.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOneBasicFieldStaticStr(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap index 3706dc15..ec4377c0 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string-4.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOneBasicFieldString(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap index 3bafdf41..29f928f3 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOneBasicFieldStringReflectBoth(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap index f0f62191..aa206779 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally-5.snap @@ -27,7 +27,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOneBasicFieldStringReflectBothEqually( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap index 300c6d0f..c775622f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_equally2-4.snap @@ -27,7 +27,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOneBasicFieldStringReflectBothEqually( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap index 9ab7ad33..0cf31683 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_string_reflectapi_both_with_attributes-5.snap @@ -27,7 +27,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicInputTestStructOneBasicFieldStringReflectBothDifferently( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") @@ -35,7 +37,9 @@ class ReflectapiDemoTestsBasicInputTestStructOneBasicFieldStringReflectBothDiffe class ReflectapiDemoTestsBasicOutputTestStructOneBasicFieldStringReflectBothD_afce1cc8( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap index c0b151a7..c4048d3c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_one_basic_field_u32-4.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOneBasicFieldU32(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap index 8a4d609e..f83fb4df 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_option-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructOption(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int | None = Field(default=None, serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap index 9af91573..4fc91540 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_tuple-5.snap @@ -26,7 +26,7 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructTuple(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict(extra="ignore", populate_by_name=True, protected_namespaces=()) 0: int 1: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap index ee1adbd0..329db22a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_additional_derives-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) us: list[None] xs: list[reflectapi_demo.tests.basic.X] @@ -34,7 +36,9 @@ class ReflectapiDemoTestsBasicTest(BaseModel): class ReflectapiDemoTestsBasicX(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsBasicY(str, Enum): diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap index 85c676c6..67400423 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_all_primitive_type_fields-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithAllPrimitiveTypeFields(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f_u8: int = Field(serialization_alias="_f_u8", validation_alias="_f_u8") f_u16: int = Field(serialization_alias="_f_u16", validation_alias="_f_u16") @@ -49,11 +51,11 @@ class ReflectapiDemoTestsBasicTestStructWithAllPrimitiveTypeFields(BaseModel): f_option: int | None = Field( default=None, serialization_alias="_f_option", validation_alias="_f_option" ) - f_vec: bytes = Field(serialization_alias="_f_vec", validation_alias="_f_vec") + f_vec: list[int] = Field(serialization_alias="_f_vec", validation_alias="_f_vec") f_hashmap: dict[int, str] = Field( serialization_alias="_f_hashmap", validation_alias="_f_hashmap" ) - f_hashset: bytes = Field( + f_hashset: list[int] = Field( serialization_alias="_f_hashset", validation_alias="_f_hashset" ) f_tuple: tuple[int, str] = Field( @@ -89,7 +91,9 @@ class ReflectapiDemoTestsBasicTestStructWithAllPrimitiveTypeFields(BaseModel): f_tuple12: tuple[int, str, int, str, int, str, int, str, int, str, int, str] = ( Field(serialization_alias="_f_tuple12", validation_alias="_f_tuple12") ) - f_array: bytes = Field(serialization_alias="_f_array", validation_alias="_f_array") + f_array: list[int] = Field( + serialization_alias="_f_array", validation_alias="_f_array" + ) f_pointer_box: int = Field( serialization_alias="_f_pointer_box", validation_alias="_f_pointer_box" ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap index 4e377f24..7d5d4bcf 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithArc(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap index c8ef15ac..b6456429 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_arc_pointer_only-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithArcPointerOnly(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f_pointer_arc: int = Field( serialization_alias="_f_pointer_arc", validation_alias="_f_pointer_arc" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap index 71e18603..d52d9b27 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_input_only-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithAttributesInputOnly(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap index 3235f52d..fc7c39d1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_attributes_output_only-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithAttributesOutputOnly(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap index 42f696d2..34120a6e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_external_generic_type_fallback-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithExternalGenericTypeFallback(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) data: dict[str, int] diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap index 87c6433f..41955c6f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_fixed_size_array-5.snap @@ -25,9 +25,11 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithFixedSizeArray(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) - f: bytes = Field(serialization_alias="_f", validation_alias="_f") + f: list[int] = Field(serialization_alias="_f", validation_alias="_f") # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap index 1bf20103..680cda89 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashmap-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithHashMap(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: dict[int, str] = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap index e83f9ae6..78f4a198 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field-5.snap @@ -25,9 +25,11 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithHashSetField(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) - f_hashset: bytes = Field( + f_hashset: list[int] = Field( serialization_alias="_f_hashset", validation_alias="_f_hashset" ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap index d5ed898e..4dd232bc 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_hashset_field_generic-5.snap @@ -31,7 +31,9 @@ G = TypeVar("G") class ReflectapiDemoTestsBasicTestStructWithHashSetFieldGeneric(BaseModel, Generic[G]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f_hashset: list[G] = Field( serialization_alias="_f_hashset", validation_alias="_f_hashset" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap index ffcace0d..0989ef4a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") class ReflectapiDemoTestsBasicTestStructWithNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.basic.TestStructNested = Field( serialization_alias="_f", validation_alias="_f" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap index 8521f6c1..31aa5f82 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_nested_external-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithNestedExternal(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.test_lib.TestStructNested = Field( serialization_alias="_f", validation_alias="_f" @@ -33,7 +35,9 @@ class ReflectapiDemoTestsBasicTestStructWithNestedExternal(BaseModel): class ReflectapiDemoTestsTestLibTestStructNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap index 443e2fd6..1974c58c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_self_via_arc-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithSelfViaArc(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.basic.TestStructWithSelfViaArc = Field( serialization_alias="_f", validation_alias="_f" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap index 922ae8b7..f1c8d2f2 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithSkipField(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap index 1edea56c..439da40f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_input-5.snap @@ -25,11 +25,15 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicInputTestStructWithSkipFieldInput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsBasicOutputTestStructWithSkipFieldInput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap index 41b03f44..20c67061 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_skip_field_output-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicInputTestStructWithSkipFieldOutput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") class ReflectapiDemoTestsBasicOutputTestStructWithSkipFieldOutput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap index c78ec3d6..3d46a32d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_array-5.snap @@ -25,9 +25,11 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTransformArray(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) - f: bytes = Field(serialization_alias="_f", validation_alias="_f") + f: list[int] = Field(serialization_alias="_f", validation_alias="_f") # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap index 0a46f70d..9d213541 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_both-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTransformBoth(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap index 6688bd7b..7f0e1c9b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTransformFallback(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap index a506cd41..e41cb0d2 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_fallback_nested-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTransformFallbackNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap index 68082964..4646424e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_input-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicInputTestStructWithTransformInput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") class ReflectapiDemoTestsBasicOutputTestStructWithTransformInput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap index a8774c94..9adb5a5c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_transform_output-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicInputTestStructWithTransformOutput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") class ReflectapiDemoTestsBasicOutputTestStructWithTransformOutput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap index c2bfe1b5..3a04c6c8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTuple(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: tuple[int, str] = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap index 8f9e250e..0c6c150a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_tuple12-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithTuple12(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: tuple[int, str, int, str, int, str, int, str, int, str, int, str] = Field( serialization_alias="_f", validation_alias="_f" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap index 79f675c8..8270cef8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec-5.snap @@ -25,9 +25,11 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithVec(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) - f: bytes = Field(serialization_alias="_f", validation_alias="_f") + f: list[int] = Field(serialization_alias="_f", validation_alias="_f") # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap index ae250e94..f1828302 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_external-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithVecExternal(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: list[reflectapi_demo.tests.test_lib.TestStructNested] = Field( serialization_alias="_f", validation_alias="_f" @@ -33,7 +35,9 @@ class ReflectapiDemoTestsBasicTestStructWithVecExternal(BaseModel): class ReflectapiDemoTestsTestLibTestStructNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap index 63f77171..5edfd824 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_nested-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithVecNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: list[list[reflectapi_demo.tests.test_lib.TestStructNested]] = Field( serialization_alias="_f", validation_alias="_f" @@ -33,7 +35,9 @@ class ReflectapiDemoTestsBasicTestStructWithVecNested(BaseModel): class ReflectapiDemoTestsTestLibTestStructNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: str = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap index 84bf6244..caf21f5c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__basic__reflectapi_struct_with_vec_two-5.snap @@ -25,9 +25,11 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsBasicTestStructWithVecTwo(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) - f: bytes = Field(serialization_alias="_f", validation_alias="_f") + f: list[int] = Field(serialization_alias="_f", validation_alias="_f") f2: list[int] = Field(serialization_alias="_f2", validation_alias="_f2") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap index 32e8f70c..8d663cbd 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_basic_variant_and_fields_and_named_fields-4.snap @@ -41,7 +41,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithBasicVariantAndFieldsAndNamedFieldsVar ): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int field_1: str @@ -52,7 +54,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithBasicVariantAndFieldsAndNamedFieldsVar ): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field1: int field2: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap index e1d592da..b1b54e13 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_empty_variant_and_fields-4.snap @@ -41,7 +41,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithEmptyVariantAndFieldsVariant2Variant( ): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap index 74faf32d..a03fa600 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_fields-4.snap @@ -39,7 +39,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsEnumsTestEnumWithFieldsVariant1Variant(BaseModel): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int @@ -47,7 +49,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithFieldsVariant1Variant(BaseModel): class ReflectapiDemoTestsEnumsTestEnumWithFieldsVariant2Variant(BaseModel): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: str field_1: float diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap index 18f37900..9141a43d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics-4.snap @@ -47,7 +47,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsVariant1Variant( ): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: T @@ -57,7 +59,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsVariant2Variant( ): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: T field_1: T diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap index 0945e562..f277e860 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields-4.snap @@ -47,7 +47,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFieldsVariant1Variant( ): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int @@ -57,7 +59,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFieldsVariant2Variant( ): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: T field_1: T diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap index c74673dc..d60917c7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__enum_with_generics_and_fields_and_named_fields-4.snap @@ -47,7 +47,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFieldsAndNamedFieldsVariant ): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int @@ -57,7 +59,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFieldsAndNamedFieldsVariant ): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: T field_1: T @@ -68,7 +72,9 @@ class ReflectapiDemoTestsEnumsTestEnumWithGenericsAndFieldsAndNamedFieldsVariant ): """Variant3 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field1: int field2: T diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap index 75856ad6..2a511e96 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_tuple_variants-5.snap @@ -31,7 +31,9 @@ class ReflectapiDemoTestsEnumsA(str, Enum): class ReflectapiDemoTestsEnumsEA(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["a"] = Field(default="a", description="Discriminator field") value: reflectapi_demo.tests.enums.A = Field(description="Tuple variant value") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap index 8e8d3fb8..0d3ee262 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__enums__internally_tagged_enum_with_unit_variants-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsEnumsAX(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["X"] = Field(default="X", description="Discriminator field") class ReflectapiDemoTestsEnumsAY(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Y"] = Field(default="Y", description="Discriminator field") value: None = Field(description="Tuple variant value") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap index 6b976659..e9fcc472 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference-4.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsGenericsTestStructWithCircularReference(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithCircularReference = Field( serialization_alias="_f", validation_alias="_f" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap index ad5b324e..ba8bc7cd 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic-4.snap @@ -33,7 +33,9 @@ T = TypeVar("T") class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGeneric( BaseModel, Generic[T] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithCircularReferenceGeneric[T] = Field( serialization_alias="_f", validation_alias="_f" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap index e0cf496d..36b035ed 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_parent-4.snap @@ -33,7 +33,9 @@ T = TypeVar("T") class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGeneric( BaseModel, Generic[T] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithCircularReferenceGeneric[T] = Field( serialization_alias="_f", validation_alias="_f" @@ -44,7 +46,9 @@ class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGeneric( class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericParent( BaseModel, Generic[T] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithCircularReferenceGeneric[ reflectapi_demo.tests.generics.TestStructWithCircularReferenceGenericParent[T] diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap index 67bc79c6..4792a72c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box-4.snap @@ -35,7 +35,9 @@ B = TypeVar("B") class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBox( BaseModel, Generic[A, B] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f1: A = Field(serialization_alias="_f1", validation_alias="_f1") f2: B = Field(serialization_alias="_f2", validation_alias="_f2") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap index 847968f0..e6193111 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent-4.snap @@ -39,7 +39,9 @@ D = TypeVar("D") class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBox( BaseModel, Generic[A, B] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f1: A = Field(serialization_alias="_f1", validation_alias="_f1") f2: B = Field(serialization_alias="_f2", validation_alias="_f2") @@ -48,7 +50,9 @@ class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBo class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithou_8e2b5cb9( BaseModel, Generic[C, D] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithCircularReferenceGenericWithoutBox[ D, C diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap index 9e198108..45754eb7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_circular_reference_generic_without_box_parent_specific-4.snap @@ -35,7 +35,9 @@ B = TypeVar("B") class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBox( BaseModel, Generic[A, B] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f1: A = Field(serialization_alias="_f1", validation_alias="_f1") f2: B = Field(serialization_alias="_f2", validation_alias="_f2") @@ -44,7 +46,9 @@ class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithoutBo class ReflectapiDemoTestsGenericsTestStructWithCircularReferenceGenericWithou_be812837( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithCircularReferenceGenericWithoutBox[ reflectapi_demo.tests.generics.TestStructWithCircularReferenceGenericWithoutBox[ diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap index 2c801f1d..1a0042bf 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct-4.snap @@ -31,7 +31,9 @@ A = TypeVar("A") class ReflectapiDemoTestsGenericsTestStructWithNestedGenericStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithSimpleGeneric[ reflectapi_demo.tests.generics.TestStructWithSimpleGeneric[int] @@ -39,7 +41,9 @@ class ReflectapiDemoTestsGenericsTestStructWithNestedGenericStruct(BaseModel): class ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric(BaseModel, Generic[A]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: A = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap index 4793191a..18643049 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_nested_generic_struct_twice-4.snap @@ -31,7 +31,9 @@ A = TypeVar("A") class ReflectapiDemoTestsGenericsTestStructWithNestedGenericStructTwice(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: reflectapi_demo.tests.generics.TestStructWithSimpleGeneric[int] = Field( serialization_alias="_f", validation_alias="_f" @@ -42,7 +44,9 @@ class ReflectapiDemoTestsGenericsTestStructWithNestedGenericStructTwice(BaseMode class ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric(BaseModel, Generic[A]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: A = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap index 6bd51ce8..12feea07 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_simple_generic-4.snap @@ -31,7 +31,9 @@ A = TypeVar("A") class ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric(BaseModel, Generic[A]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: A = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap index f066aabd..0cf7b71f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic-4.snap @@ -31,7 +31,9 @@ T = TypeVar("T") class ReflectapiDemoTestsGenericsTestStructWithVecGeneric(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: list[T] = Field(serialization_alias="_f", validation_alias="_f") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap index 121e87af..d8d94f15 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic-4.snap @@ -33,13 +33,17 @@ T = TypeVar("T") class ReflectapiDemoTestsGenericsTestStructWithSimpleGeneric(BaseModel, Generic[A]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: A = Field(serialization_alias="_f", validation_alias="_f") class ReflectapiDemoTestsGenericsTestStructWithVecGenericGeneric(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: list[reflectapi_demo.tests.generics.TestStructWithSimpleGeneric[T]] = Field( serialization_alias="_f", validation_alias="_f" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap index 4374f62b..9922e80c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__generics__struct_with_vec_generic_generic_generic-4.snap @@ -31,7 +31,9 @@ T = TypeVar("T") class ReflectapiDemoTestsGenericsTestStructWithVecGeneric(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: list[T] = Field(serialization_alias="_f", validation_alias="_f") @@ -39,7 +41,9 @@ class ReflectapiDemoTestsGenericsTestStructWithVecGeneric(BaseModel, Generic[T]) class ReflectapiDemoTestsGenericsTestStructWithVecGenericGenericGeneric( BaseModel, Generic[T] ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: list[reflectapi_demo.tests.generics.TestStructWithVecGeneric[T]] = Field( serialization_alias="_f", validation_alias="_f" @@ -73,7 +77,9 @@ class AsyncInputClient: async def test( self, data: Optional[ - reflectapi_demo.tests.generics.TestStructWithVecGenericGenericGeneric[bytes] + reflectapi_demo.tests.generics.TestStructWithVecGenericGenericGeneric[ + list[int] + ] ] = None, ) -> ApiResponse[Any]: """ @@ -117,7 +123,9 @@ class InputClient: def test( self, data: Optional[ - reflectapi_demo.tests.generics.TestStructWithVecGenericGenericGeneric[bytes] + reflectapi_demo.tests.generics.TestStructWithVecGenericGenericGeneric[ + list[int] + ] ] = None, ) -> ApiResponse[Any]: """ diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap index 3876cc9e..15e628ee 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__adj_repr_enum_with_untagged_variant-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestVariant1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant1"] = Field( default="Variant1", description="Discriminator field" @@ -35,7 +37,9 @@ class ReflectapiDemoTestsSerdeTestVariant1(BaseModel): class ReflectapiDemoTestsSerdeTestVariant2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant2"] = Field( default="Variant2", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap index 15311734..892a7b3a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__box_field_unwrapping-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTreeNode(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) label: str child: reflectapi_demo.tests.serde.TreeNode | None = None diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap index 79aa4157..3119cb23 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__datetime-5.snap @@ -27,7 +27,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) duration: ReflectapiDuration naive_time: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap index 14466478..1143d406 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_adjacently_tagged-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEmptyVariantsAdjacentlyTaggedEmpty(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["Empty"] = Field(default="Empty", description="Discriminator field") class ReflectapiDemoTestsSerdeTestEmptyVariantsAdjacentlyTaggedEmptyUnit(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["EmptyUnit"] = Field( default="EmptyUnit", description="Discriminator field" @@ -45,11 +49,15 @@ class ReflectapiDemoTestsSerdeTestEmptyVariantsAdjacentlyTaggedEmptyStructConten ): """EmptyStruct content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsSerdeTestEmptyVariantsAdjacentlyTaggedEmptyStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["EmptyStruct"] = Field( default="EmptyStruct", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap index 95573819..6809c68f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_externally_tagged-5.snap @@ -41,7 +41,9 @@ class ReflectapiDemoTestsSerdeTestEmptyVariantsExternallyTaggedEmptyUnitVariant( ): """EmptyUnit variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsSerdeTestEmptyVariantsExternallyTaggedEmptyStructVariant( @@ -49,7 +51,9 @@ class ReflectapiDemoTestsSerdeTestEmptyVariantsExternallyTaggedEmptyStructVarian ): """EmptyStruct variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Externally tagged enum using RootModel diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap index 724f0283..5fa8c888 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_internally_tagged-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEmptyVariantsInterallyTaggedEmpty(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Empty"] = Field(default="Empty", description="Discriminator field") class ReflectapiDemoTestsSerdeTestEmptyVariantsInterallyTaggedEmptyStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["EmptyStruct"] = Field( default="EmptyStruct", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap index 695c1a79..08f8a00c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__empty_variants_untagged-5.snap @@ -26,15 +26,21 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEmptyVariantsUntaggedEmpty(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsSerdeTestEmptyVariantsUntaggedEmptyUnit(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsSerdeTestEmptyVariantsUntaggedEmptyStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) ReflectapiDemoTestsSerdeTestEmptyVariantsUntagged = Union[ diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap index 36ef8a65..1cd71f20 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_mixed_variant_types_internally_tagged-5.snap @@ -26,20 +26,26 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeMixedUnit(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Unit"] = Field(default="Unit", description="Discriminator field") class ReflectapiDemoTestsSerdeMixedWrap(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Wrap"] = Field(default="Wrap", description="Discriminator field") value: str class ReflectapiDemoTestsSerdeMixedFull(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Full"] = Field(default="Full", description="Discriminator field") x: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap index 2aff9f50..ff46e578 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_all_on_variant-5.snap @@ -39,7 +39,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeTestEnumRenameAllOnVariantVariant1Variant(BaseModel): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="fieldName", validation_alias="fieldName" @@ -49,7 +51,9 @@ class ReflectapiDemoTestsSerdeTestEnumRenameAllOnVariantVariant1Variant(BaseMode class ReflectapiDemoTestsSerdeTestEnumRenameAllOnVariantVariant2Variant(BaseModel): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap index b0742651..08807994 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_rename_variant_field-5.snap @@ -39,7 +39,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeTestEnumRenameVariantFieldVariant2Variant(BaseModel): """Variant2 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) variant2_field_name: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap index a9577006..bc0c22ca 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEnumTagVariant1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant1"] = Field( default="Variant1", description="Discriminator field" @@ -35,7 +37,9 @@ class ReflectapiDemoTestsSerdeTestEnumTagVariant1(BaseModel): class ReflectapiDemoTestsSerdeTestEnumTagVariant2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant2"] = Field( default="Variant2", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap index 8b7f9035..92252421 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content-5.snap @@ -28,13 +28,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEnumTagContentVariant1Content(BaseModel): """Variant1 content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int class ReflectapiDemoTestsSerdeTestEnumTagContentVariant1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant1"] = Field( default="Variant1", description="Discriminator field" @@ -45,7 +49,9 @@ class ReflectapiDemoTestsSerdeTestEnumTagContentVariant1(BaseModel): class ReflectapiDemoTestsSerdeTestEnumTagContentVariant2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant2"] = Field( default="Variant2", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap index 1d33bd1d..36798998 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_tag_content_rename_all-5.snap @@ -28,13 +28,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEnumTagContentRenameAllVariant1Content(BaseModel): """variant1 content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int class ReflectapiDemoTestsSerdeTestEnumTagContentRenameAllVariant1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["variant1"] = Field( default="variant1", description="Discriminator field" @@ -45,7 +49,9 @@ class ReflectapiDemoTestsSerdeTestEnumTagContentRenameAllVariant1(BaseModel): class ReflectapiDemoTestsSerdeTestEnumTagContentRenameAllVariant2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["variant2"] = Field( default="variant2", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap index 5c621368..2449d71f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_untagged-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestEnumUntaggedVariant1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) value: int class ReflectapiDemoTestsSerdeTestEnumUntaggedVariant2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap index ec941ef1..48fbd558 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_field_skip-5.snap @@ -39,7 +39,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeTestEnumWithFieldSkipVariant1Variant(BaseModel): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Externally tagged enum using RootModel diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap index 3e2a6c03..1e7cdb11 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_many_variants-5.snap @@ -26,33 +26,43 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeLargeEnumAlpha(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Alpha"] = Field(default="Alpha", description="Discriminator field") class ReflectapiDemoTestsSerdeLargeEnumBeta(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Beta"] = Field(default="Beta", description="Discriminator field") class ReflectapiDemoTestsSerdeLargeEnumGamma(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Gamma"] = Field(default="Gamma", description="Discriminator field") x: int class ReflectapiDemoTestsSerdeLargeEnumDelta(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Delta"] = Field(default="Delta", description="Discriminator field") value: str class ReflectapiDemoTestsSerdeLargeEnumEpsilon(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Epsilon"] = Field( default="Epsilon", description="Discriminator field" @@ -60,46 +70,60 @@ class ReflectapiDemoTestsSerdeLargeEnumEpsilon(BaseModel): class ReflectapiDemoTestsSerdeLargeEnumZeta(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Zeta"] = Field(default="Zeta", description="Discriminator field") y: bool class ReflectapiDemoTestsSerdeLargeEnumEta(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Eta"] = Field(default="Eta", description="Discriminator field") class ReflectapiDemoTestsSerdeLargeEnumTheta(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Theta"] = Field(default="Theta", description="Discriminator field") value: int class ReflectapiDemoTestsSerdeLargeEnumIota(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Iota"] = Field(default="Iota", description="Discriminator field") class ReflectapiDemoTestsSerdeLargeEnumKappa(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Kappa"] = Field(default="Kappa", description="Discriminator field") z: float class ReflectapiDemoTestsSerdeLargeEnumLambda(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Lambda"] = Field(default="Lambda", description="Discriminator field") class ReflectapiDemoTestsSerdeLargeEnumMu(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Mu"] = Field(default="Mu", description="Discriminator field") w: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap index d32c290b..63d1c5b9 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_rename_to_invalid_chars-5.snap @@ -41,7 +41,9 @@ class ReflectapiDemoTestsSerdeTestEnumWithRenameToInvalidCharsVariant1Variant( ): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="field-name&&", validation_alias="field-name&&") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap index c11ff87a..3fda6f17 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_serde_rename_on_variants-5.snap @@ -28,13 +28,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeActionCreateItemContent(BaseModel): """create_item content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str class ReflectapiDemoTestsSerdeActionCreateItem(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["create_item"] = Field( default="create_item", description="Discriminator field" @@ -47,13 +51,17 @@ class ReflectapiDemoTestsSerdeActionCreateItem(BaseModel): class ReflectapiDemoTestsSerdeActionDeleteItemContent(BaseModel): """delete_item content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) id: int class ReflectapiDemoTestsSerdeActionDeleteItem(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["delete_item"] = Field( default="delete_item", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap index d9e12c54..6c8cf4cc 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_other-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestEnumWithVariantOtherV0(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["V0"] = Field(default="V0", description="Discriminator field") @@ -39,13 +41,17 @@ class ReflectapiDemoTestsSerdeInputTestEnumWithVariantOther(RootModel): class ReflectapiDemoTestsSerdeOutputTestEnumWithVariantOtherV0(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["V0"] = Field(default="V0", description="Discriminator field") class ReflectapiDemoTestsSerdeOutputTestEnumWithVariantOtherVariant1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Variant1"] = Field( default="Variant1", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap index d14dc6d5..ba11eb49 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__enum_with_variant_untagged-5.snap @@ -39,7 +39,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeTestEnumWithVariantUntaggedVariant1Variant(BaseModel): """Variant1 variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap index ae81e32b..c14ed57d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) index_map: dict[int, int] index_set: list[str] diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap index cc5bd94e..dd6ef2ea 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_all_python_keywords-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeKeywords(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type_: str = Field(serialization_alias="type", validation_alias="type") class_: str = Field(serialization_alias="class", validation_alias="class") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap index dd67048c..aed99566 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__field_names_with_special_chars-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeSpecialNames(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) content_type: str = Field( serialization_alias="Content-Type", validation_alias="Content-Type" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap index 4e7b6cba..d2308ac1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_adjacently_tagged_enum_field-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeMessage(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) id: str payload: reflectapi_demo.tests.serde.Payload @@ -35,13 +37,17 @@ class ReflectapiDemoTestsSerdeMessage(BaseModel): class ReflectapiDemoTestsSerdePayloadTextContent(BaseModel): """Text content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) body: str class ReflectapiDemoTestsSerdePayloadText(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["Text"] = Field(default="Text", description="Discriminator field") data: ReflectapiDemoTestsSerdePayloadTextContent = Field(description="Text content") @@ -50,13 +56,17 @@ class ReflectapiDemoTestsSerdePayloadText(BaseModel): class ReflectapiDemoTestsSerdePayloadBinaryContent(BaseModel): """Binary content""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) size: int class ReflectapiDemoTestsSerdePayloadBinary(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["Binary"] = Field(default="Binary", description="Discriminator field") data: ReflectapiDemoTestsSerdePayloadBinaryContent = Field( diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap index 16922892..97cdd7f4 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_enum_with_unit_variants_only-5.snap @@ -28,7 +28,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeItemActive(BaseModel): """'Active' variant of ReflectapiDemoTestsSerdeItem""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str status: Literal["Active"] = Field( @@ -39,7 +41,9 @@ class ReflectapiDemoTestsSerdeItemActive(BaseModel): class ReflectapiDemoTestsSerdeItemInactive(BaseModel): """'Inactive' variant of ReflectapiDemoTestsSerdeItem""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str status: Literal["Inactive"] = Field( @@ -50,7 +54,9 @@ class ReflectapiDemoTestsSerdeItemInactive(BaseModel): class ReflectapiDemoTestsSerdeItemPending(BaseModel): """'Pending' variant of ReflectapiDemoTestsSerdeItem""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str status: Literal["Pending"] = Field( @@ -70,7 +76,9 @@ class ReflectapiDemoTestsSerdeItem(RootModel): class ReflectapiDemoTestsSerdeStatusActive(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) status: Literal["Active"] = Field( default="Active", description="Discriminator field" @@ -78,7 +86,9 @@ class ReflectapiDemoTestsSerdeStatusActive(BaseModel): class ReflectapiDemoTestsSerdeStatusInactive(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) status: Literal["Inactive"] = Field( default="Inactive", description="Discriminator field" @@ -86,7 +96,9 @@ class ReflectapiDemoTestsSerdeStatusInactive(BaseModel): class ReflectapiDemoTestsSerdeStatusPending(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) status: Literal["Pending"] = Field( default="Pending", description="Discriminator field" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap index 48e5b429..0773208a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_externally_tagged_enum_field-5.snap @@ -37,7 +37,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeDrawing(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str shape: reflectapi_demo.tests.serde.Shape @@ -46,7 +48,9 @@ class ReflectapiDemoTestsSerdeDrawing(BaseModel): class ReflectapiDemoTestsSerdeShapeCircleVariant(BaseModel): """Circle variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) radius: float @@ -54,7 +58,9 @@ class ReflectapiDemoTestsSerdeShapeCircleVariant(BaseModel): class ReflectapiDemoTestsSerdeShapeRectVariant(BaseModel): """Rect variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) width: float height: float diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap index 71a8d740..473dbc10 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeA(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: int class ReflectapiDemoTestsSerdeB(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) b: int @@ -40,14 +44,18 @@ class ReflectapiDemoTestsSerdeB(BaseModel): class ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeAReflectapiDemoTestsSerdeB( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: int b: int class ReflectapiDemoTestsSerdeTestS(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["S"] = Field(default="S", description="Discriminator field") a: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap index 3b758161..7e9ea2ce 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_internally_tagged_enum_field-5.snap @@ -28,7 +28,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeOfferSingle(BaseModel): """'Single' variant of ReflectapiDemoTestsSerdeOffer""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) id: str type_: Literal["Single"] = Field( @@ -43,7 +45,9 @@ class ReflectapiDemoTestsSerdeOfferSingle(BaseModel): class ReflectapiDemoTestsSerdeOfferGroup(BaseModel): """'Group' variant of ReflectapiDemoTestsSerdeOffer""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) id: str type_: Literal["Group"] = Field( @@ -66,14 +70,18 @@ class ReflectapiDemoTestsSerdeOffer(RootModel): class ReflectapiDemoTestsSerdeOfferKindSingle(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Single"] = Field(default="Single", description="Discriminator field") business: str class ReflectapiDemoTestsSerdeOfferKindGroup(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Group"] = Field(default="Group", description="Discriminator field") count: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap index 4545d457..92103713 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_multiple_structs-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeDocument(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) title: str created_at: str @@ -35,14 +37,18 @@ class ReflectapiDemoTestsSerdeDocument(BaseModel): class ReflectapiDemoTestsSerdeMetadata(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) author: str version: int class ReflectapiDemoTestsSerdeTimestamps(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) created_at: str updated_at: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap index 575ea623..8eb43395 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_optional_internally_tagged_enum-5.snap @@ -28,7 +28,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTaskHigh(BaseModel): """'High' variant of ReflectapiDemoTestsSerdeTask""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) title: str kind: Literal["High"] = Field(default="High", description="Discriminator field") @@ -38,7 +40,9 @@ class ReflectapiDemoTestsSerdeTaskHigh(BaseModel): class ReflectapiDemoTestsSerdeTaskLow(BaseModel): """'Low' variant of ReflectapiDemoTestsSerdeTask""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) title: str kind: Literal["Low"] = Field(default="Low", description="Discriminator field") @@ -55,14 +59,18 @@ class ReflectapiDemoTestsSerdeTask(RootModel): class ReflectapiDemoTestsSerdePriorityHigh(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["High"] = Field(default="High", description="Discriminator field") deadline: str class ReflectapiDemoTestsSerdePriorityLow(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) kind: Literal["Low"] = Field(default="Low", description="Discriminator field") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap index 62abddac..7286b081 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_and_internal_enum_combined-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeAudit(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) modified_by: str @@ -34,7 +36,9 @@ class ReflectapiDemoTestsSerdeAudit(BaseModel): class ReflectapiDemoTestsSerdePostText(BaseModel): """'Text' variant of ReflectapiDemoTestsSerdePost""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) id: str modified_by: str @@ -50,7 +54,9 @@ class ReflectapiDemoTestsSerdePostText(BaseModel): class ReflectapiDemoTestsSerdePostImage(BaseModel): """'Image' variant of ReflectapiDemoTestsSerdePost""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) id: str modified_by: str @@ -75,14 +81,18 @@ class ReflectapiDemoTestsSerdePost(RootModel): class ReflectapiDemoTestsSerdeContentText(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Text"] = Field(default="Text", description="Discriminator field") body: str class ReflectapiDemoTestsSerdeContentImage(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["Image"] = Field(default="Image", description="Discriminator field") url: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap index bcb68b7e..189bdac7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_struct_with_nested_flatten-5.snap @@ -25,20 +25,26 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInner(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) z: str class ReflectapiDemoTestsSerdeMiddle(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) y: int z: str class ReflectapiDemoTestsSerdeOuter(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) x: str y: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap index 03e4ccc0..f111210f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeK(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: int class ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTupleTuple0(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap index b76f1b2f..48d17670 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_untagged_enum_field-5.snap @@ -26,20 +26,26 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeCell(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) label: str content: reflectapi_demo.tests.serde.Value class ReflectapiDemoTestsSerdeValueNum(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) value: float class ReflectapiDemoTestsSerdeValueText(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) text: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap index e7f1ccd7..b2086675 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_adjacently_tagged_enum-5.snap @@ -32,14 +32,18 @@ T = TypeVar("T") class ReflectapiDemoTestsSerdeTaggedItem(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["Item"] = Field(default="Item", description="Discriminator field") c: T class ReflectapiDemoTestsSerdeTaggedNothing(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["Nothing"] = Field(default="Nothing", description="Discriminator field") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap index b92c06da..a771a67a 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_externally_tagged_enum-5.snap @@ -45,7 +45,9 @@ T = TypeVar("T") class ReflectapiDemoTestsSerdeWrapperValueVariant(BaseModel, Generic[T]): """Value variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: T diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap index 78161cdb..8a253ce1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) inner_a: int inner_b: str @@ -41,7 +45,9 @@ class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None inner_a: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap index aeef9147..8f058a6f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap @@ -37,13 +37,17 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeTestFlattenIdent(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) job_id: int class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) payload: str @@ -51,7 +55,9 @@ class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) job_id: int payload: str @@ -62,7 +68,9 @@ class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a ): """Insert variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 @@ -72,7 +80,9 @@ class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a ): """Remove variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap index d647b027..7475c896 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int class ReflectapiDemoTestsSerdeTestLeafCollisionPair(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb b: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc @@ -41,7 +45,9 @@ class ReflectapiDemoTestsSerdeTestLeafCollisionPair(BaseModel): class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None a_field: int @@ -50,20 +56,26 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2 class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None b_field: bool class ReflectapiDemoTestsSerdeModuleASample(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a_field: int class ReflectapiDemoTestsSerdeModuleBSample(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) b_field: bool diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap index 5f792e74..11806ff8 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap @@ -26,19 +26,25 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenIdent(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) job_id: int class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) payload: str class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int @@ -46,7 +52,9 @@ class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) job_id: int payload: str @@ -55,7 +63,9 @@ class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF6 class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None job_id: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap index 800b7d0c..7c639546 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) inner_a: int inner_b: str @@ -35,7 +37,9 @@ class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): class ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde356d19e7( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int inner_a: int | None = None @@ -45,7 +49,9 @@ class ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde35 class ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int inner_a: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap index 765c333b..d3a6cff5 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap @@ -26,26 +26,34 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) inner_a: int inner_b: str class ReflectapiDemoTestsSerdeTestFlattenInnerAlt(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) alt_x: bool class ReflectapiDemoTestsSerdeTestTwoInstantiations(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 b: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20 @@ -54,7 +62,9 @@ class ReflectapiDemoTestsSerdeTestTwoInstantiations(BaseModel): class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None inner_a: int @@ -64,7 +74,9 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3a class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None alt_x: bool diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap index 36f00c64..01ef5a5e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenIdent(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) job_id: int class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) payload: str @@ -39,7 +43,9 @@ class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) job_id: int payload: str @@ -48,7 +54,9 @@ class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF6 class ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) body: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 extra: bool diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap index 6e6a6200..d9069681 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) inner_a: int inner_b: str @@ -41,7 +45,9 @@ class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): class ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None inner_a: int @@ -51,7 +57,9 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9 class ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) body: reflectapi_demo.tests.serde.TestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e extra: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_struct_repr_transparent-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_struct_repr_transparent-5.snap index 39933ef7..38cce740 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_struct_repr_transparent-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_struct_repr_transparent-5.snap @@ -32,15 +32,15 @@ class AsyncInoutClient: async def test( self, - data: Optional[bytes] = None, - ) -> ApiResponse[bytes]: + data: Optional[list[int]] = None, + ) -> ApiResponse[list[int]]: """ Args: data: Request data for the test operation. Returns: - ApiResponse[bytes]: Response containing bytes data + ApiResponse[list[int]]: Response containing list[int] data """ path = "/inout_test" @@ -49,7 +49,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=bytes, + response_model=list[int], ) @@ -74,15 +74,15 @@ class InoutClient: def test( self, - data: Optional[bytes] = None, - ) -> ApiResponse[bytes]: + data: Optional[list[int]] = None, + ) -> ApiResponse[list[int]]: """ Args: data: Request data for the test operation. Returns: - ApiResponse[bytes]: Response containing bytes data + ApiResponse[list[int]]: Response containing list[int] data """ path = "/inout_test" @@ -91,7 +91,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=bytes, + response_model=list[int], ) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap index b804d8d8..5c708dd1 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_with_concrete_flatten_not_marked-5.snap @@ -33,7 +33,9 @@ T = TypeVar("T") class ReflectapiDemoTestsSerdeTestGenericWithConcreteFlattenReflectapiDemoTes_56981a18( BaseModel ): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) other: reflectapi_demo.tests.serde.TestFlattenIfElse inner_a: int @@ -41,20 +43,26 @@ class ReflectapiDemoTestsSerdeTestGenericWithConcreteFlattenReflectapiDemoTes_56 class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) code: int class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) inner_a: int inner_b: str class ReflectapiDemoTestsSerdeTestGenericWithConcreteFlatten(BaseModel, Generic[T]): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) other: T inner_a: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap index 61341683..a412f601 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__kebab_case-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="field-name", validation_alias="field-name" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap index a0a7d86f..ce36ca73 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__multiple_underscore_prefix_fields-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeUnderscored(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) single: int = Field(serialization_alias="_single", validation_alias="_single") double: str = Field(serialization_alias="__double", validation_alias="__double") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap index a32fe6a4..af4e2543 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_deeply_nested_modules-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeDeepNestedInnerDeepType(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) data: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap index 9eb0e7ba..b0ea0886 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_single_segment_type-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeSimpleTopLevel(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) value: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap index 98cb026f..05fe16de 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__namespace_with_numeric_start-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTypeWithNumbers(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_123: str = Field(serialization_alias="123start", validation_alias="123start") kebab_field: int = Field( diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap index 56d53c09..7092e57e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_generic_containers-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeComplex(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) matrix: list[list[int]] lookup: dict[str, list[int | None]] diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap index 99f4d54e..a25edd8e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums-5.snap @@ -26,14 +26,18 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestV1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) version: Literal["v1"] = Field(default="v1", description="Discriminator field") value: reflectapi_demo.tests.serde.V1 = Field(description="Tuple variant value") class ReflectapiDemoTestsSerdeTestV2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) version: Literal["v2"] = Field(default="v2", description="Discriminator field") value: reflectapi_demo.tests.serde.V2 = Field(description="Tuple variant value") @@ -47,14 +51,18 @@ class ReflectapiDemoTestsSerdeTest(RootModel): class ReflectapiDemoTestsSerdeV1A(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["A"] = Field(default="A", description="Discriminator field") a: int class ReflectapiDemoTestsSerdeV1B(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["B"] = Field(default="B", description="Discriminator field") b: int @@ -68,14 +76,18 @@ class ReflectapiDemoTestsSerdeV1(RootModel): class ReflectapiDemoTestsSerdeV2C(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["C"] = Field(default="C", description="Discriminator field") c: int class ReflectapiDemoTestsSerdeV2D(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["D"] = Field(default="D", description="Discriminator field") d: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap index 0f98d5c9..afc38e95 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__nested_internally_tagged_enums_minimal-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestV1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) version: Literal["v1"] = Field(default="v1", description="Discriminator field") value: reflectapi_demo.tests.serde.V1 = Field(description="Tuple variant value") @@ -39,7 +41,9 @@ class ReflectapiDemoTestsSerdeTest(RootModel): class ReflectapiDemoTestsSerdeV1A(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["A"] = Field(default="A", description="Discriminator field") a: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap index be574ac6..017b721d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_adjacently_tagged-5.snap @@ -26,28 +26,36 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestNewtypeVariantsAdjacentlyTaggedInt(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["int"] = Field(default="int", description="Discriminator field") c: int class ReflectapiDemoTestsSerdeTestNewtypeVariantsAdjacentlyTaggedString(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["string"] = Field(default="string", description="Discriminator field") c: str class ReflectapiDemoTestsSerdeTestNewtypeVariantsAdjacentlyTaggedBool(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["bool"] = Field(default="bool", description="Discriminator field") c: bool class ReflectapiDemoTestsSerdeTestNewtypeVariantsAdjacentlyTaggedUnit(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) t: Literal["unit"] = Field(default="unit", description="Discriminator field") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap index 6fdfc873..dfe7813c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_externally_tagged-5.snap @@ -39,7 +39,9 @@ from reflectapi_runtime import ( class ReflectapiDemoTestsSerdeTestNewtypeVariantsExternallyTaggedIntVariant(BaseModel): """int variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: int @@ -49,7 +51,9 @@ class ReflectapiDemoTestsSerdeTestNewtypeVariantsExternallyTaggedStringVariant( ): """string variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: str @@ -57,7 +61,9 @@ class ReflectapiDemoTestsSerdeTestNewtypeVariantsExternallyTaggedStringVariant( class ReflectapiDemoTestsSerdeTestNewtypeVariantsExternallyTaggedBoolVariant(BaseModel): """bool variant""" - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_0: bool diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap index 7df27341..7eba1b16 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__newtype_variants_internally_tagged-5.snap @@ -26,21 +26,27 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeStrukt1(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) a: int b: int class ReflectapiDemoTestsSerdeStrukt2(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) c: int d: int class ReflectapiDemoTestsSerdeEnumA(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["a"] = Field(default="a", description="Discriminator field") a: int @@ -48,7 +54,9 @@ class ReflectapiDemoTestsSerdeEnumA(BaseModel): class ReflectapiDemoTestsSerdeEnumB(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) type: Literal["b"] = Field(default="b", description="Discriminator field") c: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap index 887dc718..10c7da0d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__option_of_option-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) value: str | None | None = None diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap index cd7ac8a7..99ed6227 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__self_referential_struct-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeCategory(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) name: str subcategories: list[reflectapi_demo.tests.serde.Category] diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap index 24ca2f8a..21927c1f 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_from-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructFrom(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int class ReflectapiDemoTestsSerdeTestStructFromProxy(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap index f559c5fe..49917e6c 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_into-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructInto(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int class ReflectapiDemoTestsSerdeTestStructIntoProxy(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap index 56da25bd..d04f6b43 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeMyStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap index 4c51cde7..ced0e16b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructRenameAll(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="fieldName", validation_alias="fieldName" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap index 6cc73059..6ef664f0 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_differently-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestStructRenameAllDifferently(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int class ReflectapiDemoTestsSerdeOutputTestStructRenameAllDifferently(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="fieldName", validation_alias="fieldName" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap index a1451f40..4eb5cd80 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_all_pascal_case-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructRenameAllPascalCase(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="FieldName", validation_alias="FieldName" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap index 388698d7..ff42dece 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_differently-5.snap @@ -25,11 +25,15 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeMyStructInput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsSerdeMyStructOutput(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap index d2584daa..a7d76adc 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_rename_field-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestStructRenameField(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="fieldName", validation_alias="fieldName" @@ -33,7 +35,9 @@ class ReflectapiDemoTestsSerdeInputTestStructRenameField(BaseModel): class ReflectapiDemoTestsSerdeOutputTestStructRenameField(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap index a66c446c..c1891e98 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_repr_transparent_generic_inner_type-5.snap @@ -25,9 +25,11 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTest(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) - inner: bytes + inner: list[int] # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap index c72839e1..7e663818 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_try_from-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructTryFormProxy(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int class ReflectapiDemoTestsSerdeTestStructTryFrom(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap index 08cbcd82..b751e5fb 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructWithFlatten(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int class ReflectapiDemoTestsSerdeTestStructWithFlattenNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap index 683e9e7b..9ac2ad9d 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructWithFlattenNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int class ReflectapiDemoTestsSerdeTestStructWithFlattenOptional(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int | None = None diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap index 3f443e69..730aa594 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_flatten_optional_and_required-5.snap @@ -26,7 +26,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructRenameAll(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="fieldName", validation_alias="fieldName" @@ -34,13 +36,17 @@ class ReflectapiDemoTestsSerdeTestStructRenameAll(BaseModel): class ReflectapiDemoTestsSerdeTestStructWithFlattenNested(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int class ReflectapiDemoTestsSerdeTestStructWithFlattenOptionalAndRequired(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int | None = None field_name: int = Field( diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap index 15e160c9..5399a5bc 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_invalid_chars-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructWithRenameToInvalidChars(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="field-name&&", validation_alias="field-name&&") diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap index f7949de7..9e2e76c0 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_rename_to_kebab_case-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeStructName(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) field_name: int = Field( serialization_alias="field-name", validation_alias="field-name" diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap index 7d84b845..9b1e6682 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_default-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestStructWithSerdeDefault(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int | None = None class ReflectapiDemoTestsSerdeOutputTestStructWithSerdeDefault(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap index 858986f6..b843f761 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStructWithSerdeSkip(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap index 09f9642e..ac8b0f16 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_deserialize-5.snap @@ -25,11 +25,15 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestStructWithSerdeSkipDeserialize(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) class ReflectapiDemoTestsSerdeOutputTestStructWithSerdeSkipDeserialize(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap index 9736940c..a50c0eee 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize-5.snap @@ -25,13 +25,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestStructWithSerdeSkipSerialize(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int = Field(serialization_alias="_f", validation_alias="_f") class ReflectapiDemoTestsSerdeOutputTestStructWithSerdeSkipSerialize(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) # Namespace classes for dotted access to types diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap index e5156189..c6f69987 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__struct_with_serde_skip_serialize_if-5.snap @@ -26,13 +26,17 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeInputTestStructWithSerdeSkipSerializeIf(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int | None = None class ReflectapiDemoTestsSerdeOutputTestStructWithSerdeSkipSerializeIf(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) f: int | None = None diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap index 3580a38a..0c1d647b 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__timezone-5.snap @@ -25,7 +25,9 @@ from reflectapi_runtime import ReflectapiInfallible class ReflectapiDemoTestsSerdeTestStruct(BaseModel): - model_config = ConfigDict(extra="ignore", populate_by_name=True) + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) timezone: str diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap index 87a593ab..73bc353e 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap @@ -10,16 +10,16 @@ expression: s "version": "1.0.0" }, "paths": { - "/codegen-regression": { - "description": "Regression-test endpoint pinning codegen bugs", + "/codegen-coverage": { + "description": "Coverage fixtures for codegen edge cases", "post": { - "operationId": "codegen-regression", - "description": "Regression-test endpoint pinning codegen bugs", + "operationId": "codegen-coverage", + "description": "Coverage fixtures for codegen edge cases", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/myapi.CodegenRegressionRequest" + "$ref": "#/components/schemas/myapi.coverage.CoverageRequest" } } }, @@ -31,7 +31,36 @@ expression: s "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/myapi.CodegenRegressionResponse" + "$ref": "#/components/schemas/myapi.coverage.CoverageResponse" + } + } + } + } + } + } + }, + "/codegen-order-coverage": { + "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", + "post": { + "operationId": "codegen-order-coverage", + "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.OrderCoverageRequest" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/myapi.OrderCoverageResponse" } } } @@ -422,9 +451,17 @@ expression: s "description": "64-bit floating point number", "type": "number" }, - "myapi.CodegenRegressionRequest": { + "i32": { + "description": "32-bit signed integer", + "type": "integer" + }, + "i64": { + "description": "64-bit signed integer", + "type": "integer" + }, + "myapi.OrderCoverageRequest": { "type": "object", - "title": "myapi.CodegenRegressionRequest", + "title": "myapi.OrderCoverageRequest", "required": [ "order", "policy", @@ -432,11 +469,10 @@ expression: s ], "properties": { "order": { - "description": "Pulls in `order::OrderInsertData` (bug 1) and Rust tuple\n(bug 2) reachability.", "$ref": "#/components/schemas/myapi.order.OrderInsertData" }, "policy": { - "description": "Pulls in `order::Policy` for PhantomData (bug 4).", + "description": "Exercises `PhantomData` elision (the field carries no wire\ndata and must not appear in the Python model).", "type": "object", "title": "myapi.order.Policy", "required": [ @@ -459,14 +495,144 @@ expression: s } }, "rate_limit": { - "description": "Pulls in `order::RateLimit` for Duration (bug 3).", "$ref": "#/components/schemas/myapi.order.RateLimit" } } }, - "myapi.CodegenRegressionResponse": { + "myapi.OrderCoverageResponse": { + "type": "object", + "title": "myapi.OrderCoverageResponse", + "required": [ + "ok" + ], + "properties": { + "ok": { + "$ref": "#/components/schemas/bool" + } + } + }, + "myapi.coverage.BaseModel": { + "type": "object", + "title": "myapi.coverage.BaseModel", + "required": [ + "label" + ], + "properties": { + "label": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.CoverageRequest": { + "type": "object", + "title": "myapi.coverage.CoverageRequest", + "required": [ + "deep_option", + "defaulted", + "empty", + "generic_tree", + "int_keyed", + "keyword_variants", + "keywords", + "mutual", + "reserved", + "shadow_base_model", + "shadowing", + "tree", + "user_id", + "weird_doc", + "wrapper_int", + "wrapper_str" + ], + "properties": { + "deep_option": { + "$ref": "#/components/schemas/myapi.coverage.DeepOption" + }, + "defaulted": { + "$ref": "#/components/schemas/myapi.coverage.DefaultedField" + }, + "empty": { + "$ref": "#/components/schemas/myapi.coverage.EmptyStruct" + }, + "generic_tree": { + "type": "object", + "title": "myapi.coverage.GenericTree", + "required": [ + "children", + "value" + ], + "properties": { + "children": { + "description": "Expandable array type", + "type": "array", + "items": { + "$ref": "#/components/schemas/myapi.coverage.GenericTree_i32_" + } + }, + "value": { + "$ref": "#/components/schemas/i32" + } + } + }, + "int_keyed": { + "$ref": "#/components/schemas/myapi.coverage.IntKeyedMap" + }, + "keyword_variants": { + "$ref": "#/components/schemas/myapi.coverage.KeywordVariants" + }, + "keywords": { + "$ref": "#/components/schemas/myapi.coverage.PyKeywordFields" + }, + "mutual": { + "$ref": "#/components/schemas/myapi.coverage.MutualA" + }, + "reserved": { + "$ref": "#/components/schemas/myapi.coverage.PydanticReservedFields" + }, + "shadow_base_model": { + "$ref": "#/components/schemas/myapi.coverage.BaseModel" + }, + "shadowing": { + "$ref": "#/components/schemas/myapi.coverage.ShadowingFields" + }, + "tree": { + "$ref": "#/components/schemas/myapi.coverage.TreeNode" + }, + "user_id": { + "$ref": "#/components/schemas/u64" + }, + "weird_doc": { + "$ref": "#/components/schemas/myapi.coverage.WeirdDocstring" + }, + "wrapper_int": { + "type": "object", + "title": "myapi.coverage.Wrapper", + "required": [ + "inner" + ], + "properties": { + "inner": { + "$ref": "#/components/schemas/i32" + } + } + }, + "wrapper_str": { + "type": "object", + "title": "myapi.coverage.Wrapper", + "required": [ + "inner" + ], + "properties": { + "inner": { + "$ref": "#/components/schemas/std.string.String" + } + } + } + } + }, + "myapi.coverage.CoverageResponse": { "type": "object", - "title": "myapi.CodegenRegressionResponse", + "title": "myapi.coverage.CoverageResponse", "required": [ "ok" ], @@ -476,6 +642,325 @@ expression: s } } }, + "myapi.coverage.DeepOption": { + "type": "object", + "title": "myapi.coverage.DeepOption", + "required": [ + "maybe_maybe" + ], + "properties": { + "maybe_maybe": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/std.string.String" + } + ] + } + ] + } + } + }, + "myapi.coverage.DefaultedField": { + "type": "object", + "title": "myapi.coverage.DefaultedField", + "properties": { + "count": { + "$ref": "#/components/schemas/u32" + } + } + }, + "myapi.coverage.EmptyStruct": { + "type": "object", + "title": "myapi.coverage.EmptyStruct", + "properties": {} + }, + "myapi.coverage.IntKeyedMap": { + "type": "object", + "title": "myapi.coverage.IntKeyedMap", + "required": [ + "by_id", + "uuid_keyed" + ], + "properties": { + "by_id": { + "description": "Key-value map type", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/std.string.String" + } + }, + "uuid_keyed": { + "description": "Key-value map type", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/std.string.String" + } + } + } + }, + "myapi.coverage.KeywordVariants": { + "oneOf": [ + { + "type": "object", + "title": "class", + "required": [ + "kind" + ], + "properties": { + "kind": { + "const": "class" + } + } + }, + { + "type": "object", + "title": "lambda", + "required": [ + "kind", + "name" + ], + "properties": { + "kind": { + "const": "lambda" + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + { + "type": "object", + "title": "return", + "required": [ + "kind", + "value" + ], + "properties": { + "kind": { + "const": "return" + }, + "value": { + "$ref": "#/components/schemas/i32" + } + } + } + ] + }, + "myapi.coverage.MutualA": { + "type": "object", + "title": "myapi.coverage.MutualA", + "required": [ + "b", + "name" + ], + "properties": { + "b": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/myapi.coverage.MutualB" + } + ] + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.MutualB": { + "type": "object", + "title": "myapi.coverage.MutualB", + "required": [ + "a", + "name" + ], + "properties": { + "a": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/myapi.coverage.MutualA" + } + ] + }, + "name": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.PyKeywordFields": { + "type": "object", + "title": "myapi.coverage.PyKeywordFields", + "required": [ + "None", + "True", + "class", + "from", + "import", + "lambda", + "match", + "return", + "type", + "yield" + ], + "properties": { + "None": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/i32" + } + ] + }, + "True": { + "$ref": "#/components/schemas/bool" + }, + "class": { + "$ref": "#/components/schemas/std.string.String" + }, + "from": { + "$ref": "#/components/schemas/u32" + }, + "import": { + "$ref": "#/components/schemas/bool" + }, + "lambda": { + "$ref": "#/components/schemas/i64" + }, + "match": { + "$ref": "#/components/schemas/std.string.String" + }, + "return": { + "description": "Expandable array type", + "type": "array", + "items": { + "$ref": "#/components/schemas/u8" + } + }, + "type": { + "$ref": "#/components/schemas/std.string.String" + }, + "yield": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/std.string.String" + } + ] + } + } + }, + "myapi.coverage.PydanticReservedFields": { + "type": "object", + "title": "myapi.coverage.PydanticReservedFields", + "required": [ + "model_config", + "model_dump_json", + "model_fields_set" + ], + "properties": { + "model_config": { + "$ref": "#/components/schemas/std.string.String" + }, + "model_dump_json": { + "$ref": "#/components/schemas/std.string.String" + }, + "model_fields_set": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.ShadowingFields": { + "type": "object", + "title": "myapi.coverage.ShadowingFields", + "required": [ + "annotated", + "base_model", + "field", + "generic" + ], + "properties": { + "annotated": { + "$ref": "#/components/schemas/std.string.String" + }, + "base_model": { + "$ref": "#/components/schemas/std.string.String" + }, + "field": { + "$ref": "#/components/schemas/std.string.String" + }, + "generic": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.TreeNode": { + "type": "object", + "title": "myapi.coverage.TreeNode", + "required": [ + "children", + "parent", + "value" + ], + "properties": { + "children": { + "description": "Expandable array type", + "type": "array", + "items": { + "$ref": "#/components/schemas/myapi.coverage.TreeNode" + } + }, + "parent": { + "oneOf": [ + { + "description": "Null", + "type": "null" + }, + { + "$ref": "#/components/schemas/myapi.coverage.TreeNode" + } + ] + }, + "value": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, + "myapi.coverage.WeirdDocstring": { + "description": "A docstring with \\\"quotes\\\" and \\'apostrophes\\' and a backslash: \\\\\\\\\nAnd a \\\"\\\"\\\"triple quote\\\"\\\"\\\" inside.", + "type": "object", + "title": "myapi.coverage.WeirdDocstring", + "required": [ + "value" + ], + "properties": { + "value": { + "$ref": "#/components/schemas/std.string.String" + } + } + }, "myapi.model.Behavior": { "oneOf": [ { @@ -669,7 +1154,7 @@ expression: s } }, "myapi.order.OrderInsertData": { - "description": "Bug 1 (namespace alias mismatch). The struct name starts with\nthe parent namespace\\'s cap (`Order…`) — the alias-stripping\npass used to strip the leading cap from the namespace alias\n(`order.InsertData = OrderInsertData`) while field annotations\nkept the un-stripped form, producing `order.OrderInsertData`\nwhich doesn\\'t resolve at `model_rebuild()` time.", + "description": "A struct whose name begins with the parent namespace cap.\nExercises the namespace-alias path for both the stripped\n(`order.InsertData`) and Rust-leaf (`order.OrderInsertData`)\nforms.", "type": "object", "title": "myapi.order.OrderInsertData", "required": [ @@ -678,7 +1163,7 @@ expression: s ], "properties": { "alternative_part_number": { - "description": "Bug 2 (tuple types). serde emits Rust tuples as JSON arrays;\nthe Python codegen used to emit `std.tuple.Tuple2[...]`\nwith no matching class, so any reference broke at rebuild.", + "description": "Exercises the `tuple[A, B]` rendering for `(A, B)`.", "oneOf": [ { "description": "Null", @@ -704,7 +1189,7 @@ expression: s } }, "myapi.order.RateLimit": { - "description": "Bug 3 (Duration shape). serde emits `Duration` as\n`{\\\"secs\\\": , \\\"nanos\\\": }`. Pydantic\\'s `timedelta`\nvalidator rejects that shape, so any response with a Duration\nfailed validation.", + "description": "Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire\nadapter.", "type": "object", "title": "myapi.order.RateLimit", "required": [ diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/partial.py b/reflectapi-python-runtime/src/reflectapi_runtime/partial.py index edd61701..024f48a6 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/partial.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/partial.py @@ -1,28 +1,23 @@ -"""Partial-model base class for reflectapi-generated Pydantic models. - -reflectapi schemas distinguish three states for nullable fields declared -as ``reflectapi::Option``: ``Some(value)``, explicit ``null``, and -"key absent on the wire" (``Undefined``). TypeScript clients express -this natively (``field?: T | null``); Rust clients use the -``reflectapi::Option`` enum. Python clients used to ship a custom -``ReflectapiOption[T]`` wrapper class to surface the same distinction. - -The wrapper duplicated information Pydantic already tracks via -``model_fields_set`` and forced users to learn a parallel -``.unwrap`` / ``.is_undefined`` API. This module replaces it with a -``BaseModel`` mixin that: - -- leaves field types as plain ``T | None`` (so Pydantic validates them - exactly as it would any other nullable field), +"""Base class for generated Pydantic models with partial fields. + +reflectapi schemas distinguish three states for nullable fields +declared as ``reflectapi::Option``: ``Some(value)``, explicit +``null``, and "key absent on the wire" (``Undefined``). TypeScript +clients express this natively (``field?: T | null``); Rust clients use +the ``reflectapi::Option`` enum. The Python equivalent uses +``ReflectapiPartialModel`` — a ``BaseModel`` mixin that: + +- leaves field types as plain ``T | None``, so Pydantic validates them + exactly as it would any other nullable field; - relies on ``model_fields_set`` — Pydantic's built-in record of which - fields were *actually provided* during deserialise / construction — - as the source of truth for "was this on the wire?", + fields were actually provided during deserialise or construction — + as the source of truth for "was this on the wire?"; - emits a wire payload that omits keys not in ``model_fields_set``, preserving the absent-vs-null distinction round-trip. -Codegen marks every generated class with at least one -``reflectapi::Option`` field as inheriting from -:class:`ReflectapiPartialModel` instead of :class:`pydantic.BaseModel`. +Codegen makes every generated class with at least one +``reflectapi::Option`` field inherit from this class instead of +``pydantic.BaseModel``. """ from __future__ import annotations diff --git a/reflectapi-python-runtime/tests/test_codegen_regressions.py b/reflectapi-python-runtime/tests/test_codegen_regressions.py index 86fd7f8c..b31e28a6 100644 --- a/reflectapi-python-runtime/tests/test_codegen_regressions.py +++ b/reflectapi-python-runtime/tests/test_codegen_regressions.py @@ -1,14 +1,8 @@ -"""Regression tests for runtime types that fix codegen bugs. +"""Contract tests for runtime types consumed by the Python codegen. -These tests pin the runtime contracts behind four user-reported bugs in -the Python codegen. They live in the runtime test suite — alongside -``ReflectapiPartialModel`` and the rest of the runtime API — so they -run on every push without needing a full end-to-end demo-client -regeneration. - -Each test is named after the bug it pins and includes a brief comment -linking it to the failure mode it locks down. The complementary end-to- -end check (regenerate the demo client + import it strictly) lives in +These tests pin the runtime API the generated client depends on. They +run on every push without needing a full demo-client regeneration; the +complementary end-to-end import check lives in ``.github/workflows/ci.yaml::python-codegen-smoke``. """ @@ -24,10 +18,8 @@ class TestDurationRoundTrip: - """Bug 3: serde emits ``Duration`` as ``{"secs": …, "nanos": …}``; - Pydantic's bare ``timedelta`` validator rejects that shape. The - ``ReflectapiDuration`` Annotated type round-trips both directions. - """ + """``ReflectapiDuration`` round-trips serde's ``{secs, nanos}`` + wire shape through a Python ``timedelta``.""" def _model(self): class M(BaseModel): diff --git a/reflectapi-python-runtime/tests/test_edge_cases.py b/reflectapi-python-runtime/tests/test_edge_cases.py index a8864cf0..ab74805e 100644 --- a/reflectapi-python-runtime/tests/test_edge_cases.py +++ b/reflectapi-python-runtime/tests/test_edge_cases.py @@ -141,10 +141,6 @@ def test_request_with_circular_reference_in_json_data(self, mock_send): client = ClientBase("https://api.example.com") # `json.dumps` detects circular references and raises ValueError. - # (The previous implementation hit Python's recursion limit - # because it pre-traversed the dict; the current path delegates - # directly to the JSON encoder, which surfaces the error sooner - # with a clearer message.) with pytest.raises(ValueError, match="Circular reference"): client._make_request("/test", json_data=data) diff --git a/reflectapi-python-runtime/tests/test_partial.py b/reflectapi-python-runtime/tests/test_partial.py index 8873c577..7142e35f 100644 --- a/reflectapi-python-runtime/tests/test_partial.py +++ b/reflectapi-python-runtime/tests/test_partial.py @@ -1,11 +1,12 @@ """Tests for ``ReflectapiPartialModel``. -These tests pin the wire-format contract that replaced the now-deleted -``ReflectapiOption`` class: a field that was *never explicitly set* on -the instance is omitted from the serialised payload entirely; a field -set to ``None`` is emitted as ``null``; a field set to a value is -emitted as that value. Round-trip preserves the distinction via -``model_fields_set``. +Contract: + +* A field that was never explicitly set on the instance is omitted + from the serialised payload entirely. +* A field set to ``None`` is emitted as ``null``. +* A field set to a value is emitted as that value. +* Round-trip preserves the distinction via ``model_fields_set``. """ from __future__ import annotations @@ -93,14 +94,11 @@ def test_value_appears_on_wire(self): } def test_post_construction_assignment_lands_on_wire(self): - """Caveat: requires ``validate_assignment=True`` in ``model_config``. - - Generated client classes always emit that flag; constructing a - ``ReflectapiPartialModel`` subclass in user code without it (this - base ``Item`` model_config doesn't include it) leaves - ``model_fields_set`` untouched on assignment. This test pins the - behaviour for the *generated* shape — we patch the config locally - to match what the codegen emits. + """Post-construction assignment marks the field as set. + + Requires ``validate_assignment=True`` in ``model_config`` — + generated client classes always include it, so attribute + writes after construction land on the wire as expected. """ class WithAssign(ReflectapiPartialModel): @@ -150,8 +148,8 @@ def test_garbage_inner_data_rejected(self): Item.model_validate( {"identity": "x", "snapshot": {"wrong_field": True}} ) - # The error path includes 'snapshot.name' to show the inner - # validator ran. Pre-fix it would have silently accepted the dict. + # The error path includes 'snapshot.name', proving the inner + # type's validator ran on the wire payload. loc = exc_info.value.errors()[0]["loc"] assert "snapshot" in loc and "name" in loc diff --git a/reflectapi/src/codegen/openapi.rs b/reflectapi/src/codegen/openapi.rs index 7d803e9f..32d886c7 100644 --- a/reflectapi/src/codegen/openapi.rs +++ b/reflectapi/src/codegen/openapi.rs @@ -56,6 +56,7 @@ pub fn generate_spec(schema: &crate::Schema, config: &Config) -> Spec { Converter { config, components: Default::default(), + in_progress: Default::default(), } .convert(schema) } @@ -65,6 +66,7 @@ impl From<&crate::Schema> for Spec { Converter { config: &Config::default(), components: Default::default(), + in_progress: Default::default(), } .convert(schema) } @@ -422,6 +424,14 @@ impl Ref { struct Converter<'a> { components: Components, config: &'a Config, + /// Type references currently being converted. Keyed by + /// `fmt_type_ref` so different monomorphizations of the same + /// generic (e.g. `Tree` vs `Tree`) are tracked + /// independently. Required to break recursion for generic + /// self-referential types, which are inlined rather than + /// promoted to `components.schemas` and so don't benefit from + /// the stub-on-insert guard elsewhere in this module. + in_progress: std::collections::HashSet, } impl Converter<'_> { @@ -605,6 +615,39 @@ impl Converter<'_> { return InlineOrRef::Ref(schema_ref); } + // Recursion guard for generic self-referential types. Non- + // generic types are guarded by the stub-insert into + // `components.schemas` below, which the early-return at the + // top of this function uses to break re-entry. Generic types + // are inlined into their use sites and so don't appear in + // `components.schemas` at all — they need a separate + // in-progress set keyed by the full monomorphization, so + // distinct instantiations of the same generic are tracked + // independently. + let in_progress_key = fmt_type_ref(ty_ref); + if self.in_progress.contains(&in_progress_key) { + // Emit a placeholder ref. The target isn't a real + // component (generics aren't promoted to named + // components), but the schema remains parseable as + // documentation and bounded in size. + let safe_key = in_progress_key.replace(['<', '>'], "_").replace(", ", "_"); + return InlineOrRef::Ref(Ref::new(format!("#/components/schemas/{safe_key}"))); + } + self.in_progress.insert(in_progress_key.clone()); + + let result = self.convert_type_ref_inner(schema, kind, ty_ref, name); + self.in_progress.remove(&in_progress_key); + result + } + + fn convert_type_ref_inner( + &mut self, + schema: &crate::Schema, + kind: Kind, + ty_ref: &crate::TypeReference, + name: String, + ) -> InlineOrRef { + let schema_ref = Ref::new(format!("#/components/schemas/{name}")); let reflect_ty = match kind { Kind::Input => schema.input_types.get_type(&ty_ref.name), Kind::Output => schema.output_types.get_type(&ty_ref.name), diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index ed32457a..1e9678f5 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -1553,13 +1553,10 @@ fn build_python_generation( mut schema: Schema, config: &Config, ) -> anyhow::Result { - // PhantomData carries no wire data in Rust. serde serialises - // each occurrence as `null`, but the field still appears in the - // reflectapi schema with `type_ref.name == "std::marker::PhantomData"`. - // Render-time the codegen used to emit - // `std.marker.PhantomData[T]`, a dangling reference that broke - // `model_rebuild()`. Strip the fields entirely — they have no - // observable behaviour for a Python client. + // `PhantomData` is a Rust-only type-system marker — it carries + // no wire data. Strip every such field before rendering so the + // Python model doesn't reference a non-existent + // `std.marker.PhantomData`. strip_phantom_data_fields(&mut schema); // Consolidate input/output types FIRST so both the SemanticSchema and @@ -2235,13 +2232,10 @@ fn render_module_file( if !body.trim().is_empty() { body.push('\n'); } - // Narrow the suppression to `ImportError` only: the wide - // `except Exception: pass` here used to swallow every - // `model_rebuild()` failure raised by `_rebuild_models()`, - // which is the same class of bug we want to surface. The - // remaining ImportError suppression covers the legitimate case - // where this namespace is imported standalone before the root - // package finishes initialising. + // Suppress only `ImportError` (which can occur if the + // namespace is imported standalone before the root package + // finishes initialising) — let `model_rebuild()` failures + // propagate so dangling references surface at import time. body.push_str("try:\n"); body.push_str(" from .._rebuild import rebuild_models as _rebuild_models\n\n"); body.push_str(" _rebuild_models()\n"); @@ -4672,19 +4666,58 @@ fn to_screaming_snake_case(s: &str) -> String { to_snake_case(s).to_uppercase() } +/// Pydantic v2 methods that we must not shadow with a field of the +/// same name. If a user has a field named exactly one of these on the +/// wire, we have to rename the Python attribute and carry the wire +/// name as an alias. The `model_` prefix conflict more broadly is +/// silenced via `protected_namespaces=()` in the generated +/// ConfigDict — that's the lighter-touch fix for the +/// `model_` namespace; this list is the heavier-touch fix +/// for names that would also shadow real methods at the instance level. +fn is_pydantic_method_name(name: &str) -> bool { + matches!( + name, + "model_dump" + | "model_dump_json" + | "model_validate" + | "model_validate_json" + | "model_validate_strings" + | "model_construct" + | "model_copy" + | "model_rebuild" + | "model_json_schema" + | "model_parametrized_name" + | "model_post_init" + | "model_config" + | "model_fields" + | "model_fields_set" + | "model_extra" + | "model_computed_fields" + ) +} + fn sanitize_field_name_with_alias(name: &str, serde_name: &str) -> (String, Option) { let snake_case = to_snake_case(name); let sanitized = safe_python_identifier(&snake_case); // Strip leading underscores - Pydantic v2 treats _-prefixed fields as private let sanitized = sanitized.trim_start_matches('_').to_string(); - let sanitized = if sanitized.is_empty() { + let mut sanitized = if sanitized.is_empty() { // Edge case: name was all underscores "field".to_string() } else { sanitized }; + // If the field exact-matches a Pydantic v2 method/attribute name, + // shadowing it at the instance level breaks `.model_dump_json()` + // and friends. Suffix with `_` so the field is reachable as + // `obj.model_dump_json_` while the wire name (`model_dump_json`) + // continues to round-trip via the alias. + if is_pydantic_method_name(&sanitized) { + sanitized.push('_'); + } + // The wire name (serde_name) is what goes over JSON. // We need an alias whenever the Python field name differs from the wire name. let wire_name = serde_name; @@ -4723,13 +4756,16 @@ fn type_ref_to_python_type( return Ok(python_type.clone()); } - // Special case: Vec should be bytes in Python - if type_ref.name == "std::vec::Vec" && type_ref.arguments.len() == 1 { - let arg = &type_ref.arguments[0]; - if arg.name == "u8" { - return Ok("bytes".to_string()); - } - } + // Note on `Vec`: serde's *default* JSON representation is + // a JSON array of integers, **not** a base64-encoded string. + // Rendering it as Python `bytes` would force users to base64- + // encode their data manually and confuse Pydantic's validator + // (which expects a base64 string for `bytes`). The + // base64-encoded form requires the user to opt in on the Rust + // side via the `serde_bytes` crate, which surfaces as a + // different schema type entirely. Until that's modelled + // explicitly we keep `Vec` as `list[int]` — matches what + // the wire actually carries. if python_type_mappings() .get(type_ref.name.as_str()) @@ -5340,10 +5376,16 @@ pub mod templates { // `m.field = value` after construction (so the assignment // actually lands on the wire). Plain BaseModel classes // don't need it. + // `protected_namespaces=()` disables Pydantic's prefix + // check (it defaults to `("model_",)` so any user field + // starting with `model_` would raise at class-construction + // time). `is_pydantic_method_name` already renames the + // exact-match collisions; this kills the warning for the + // remaining safe-but-prefixed cases. let config = if self.is_partial() { - "ConfigDict(extra=\"ignore\", populate_by_name=True, validate_assignment=True)" + "ConfigDict(extra=\"ignore\", populate_by_name=True, validate_assignment=True, protected_namespaces=())" } else { - "ConfigDict(extra=\"ignore\", populate_by_name=True)" + "ConfigDict(extra=\"ignore\", populate_by_name=True, protected_namespaces=())" }; writeln!(s, " model_config = {config}").unwrap(); writeln!(s).unwrap(); @@ -6043,7 +6085,11 @@ pub mod templates { ) .unwrap(); } - writeln!(s, " model_config = ConfigDict(extra=\"ignore\")").unwrap(); + writeln!( + s, + " model_config = ConfigDict(extra=\"ignore\", protected_namespaces=())" + ) + .unwrap(); writeln!(s).unwrap(); writeln!(s, " def model_dump(self, **kwargs):").unwrap(); writeln!( @@ -6095,7 +6141,11 @@ pub mod templates { .unwrap(); } writeln!(s).unwrap(); - writeln!(s, " model_config = ConfigDict(extra=\"ignore\")").unwrap(); + writeln!( + s, + " model_config = ConfigDict(extra=\"ignore\", protected_namespaces=())" + ) + .unwrap(); writeln!(s).unwrap(); for field in &self.fields { write!(s, " {}: {}", field.name, field.type_annotation).unwrap(); @@ -6162,7 +6212,11 @@ pub mod templates { ) .unwrap(); } - writeln!(s, " model_config = ConfigDict(extra=\"ignore\")").unwrap(); + writeln!( + s, + " model_config = ConfigDict(extra=\"ignore\", protected_namespaces=())" + ) + .unwrap(); writeln!(s).unwrap(); for field in &self.fields { write!(s, " {}: {}", field.name, field.type_annotation).unwrap(); From c22f87f1bcf277792319d6c1b38f8aa187b1900a Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 12:16:45 +1200 Subject: [PATCH 05/17] chore(demo): drop leftover adversarial namespace from earlier regen --- .../api_client/myapi/adversarial/__init__.py | 345 ------------------ 1 file changed, 345 deletions(-) delete mode 100644 reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py diff --git a/reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py deleted file mode 100644 index 08545f96..00000000 --- a/reflectapi-demo/clients/python/api_client/myapi/adversarial/__init__.py +++ /dev/null @@ -1,345 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Demo application -This is a demo application -""" - -from __future__ import annotations - - -# Standard library imports -import warnings -from collections.abc import AsyncIterator, Iterator -from datetime import datetime -from enum import Enum -from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union - -# Third-party imports -from pydantic import ( - BaseModel, - ConfigDict, - Field, - RootModel, - model_serializer, - model_validator, -) - -# Runtime imports -from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration -from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible -from reflectapi_runtime import ReflectapiPartialModel -from reflectapi_runtime import ( - parse_externally_tagged as _parse_externally_tagged, - serialize_externally_tagged as _serialize_externally_tagged, -) - - -# Type variables for generic types - - -C = TypeVar("C") - -T = TypeVar("T") - - -# External type definitions -StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] -StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] -StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] -StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] - - -class MyapiAdversarialRequest(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - keywords: myapi.adversarial.PyKeywordFields - reserved: myapi.adversarial.PydanticReservedFields - tree: myapi.adversarial.TreeNode - mutual: myapi.adversarial.MutualA - generic_tree: myapi.adversarial.GenericTree[int] - keyword_variants: myapi.adversarial.KeywordVariants - int_keyed: myapi.adversarial.IntKeyedMap - user_id: int - deep_option: myapi.adversarial.DeepOption - shadowing: myapi.adversarial.ShadowingFields - empty: myapi.adversarial.EmptyStruct - weird_doc: myapi.adversarial.WeirdDocstring - shadow_base_model: myapi.adversarial.BaseModel - wrapper_int: myapi.adversarial.Wrapper[int] - wrapper_str: myapi.adversarial.Wrapper[str] - defaulted: myapi.adversarial.DefaultedField - - -class MyapiAdversarialResponse(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - ok: bool - - -class MyapiAdversarialBaseModel(BaseModel): - """The generated module imports `BaseModel`, `Field`, `Annotated`, - `Generic`, `TypeVar` from `pydantic`/`typing`. A user struct - with one of those names must not break the module.""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - label: str - - -class MyapiAdversarialDeepOption(ReflectapiPartialModel): - model_config = ConfigDict( - extra="ignore", - populate_by_name=True, - validate_assignment=True, - protected_namespaces=(), - ) - - maybe_maybe: str | None | None = None - - -class MyapiAdversarialDefaultedField(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - count: int | None = None - - -class MyapiAdversarialEmptyStruct(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - -class MyapiAdversarialGenericTree(BaseModel, Generic[T]): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: T - children: list[myapi.adversarial.GenericTree[T]] - - -class MyapiAdversarialIntKeyedMap(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - by_id: dict[int, str] - uuid_keyed: dict[str, str] - - -class MyapiAdversarialMutualA(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - name: str - b: myapi.adversarial.MutualB | None = None - - -class MyapiAdversarialMutualB(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - name: str - a: myapi.adversarial.MutualA | None = None - - -class MyapiAdversarialPyKeywordFields(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - class_: str = Field(serialization_alias="class", validation_alias="class") - from_: int = Field(serialization_alias="from", validation_alias="from") - import_: bool = Field(serialization_alias="import", validation_alias="import") - lambda_: int = Field(serialization_alias="lambda", validation_alias="lambda") - return_: list[int] = Field(serialization_alias="return", validation_alias="return") - yield_: str | None = Field( - default=None, serialization_alias="yield", validation_alias="yield" - ) - none: int | None = Field( - default=None, serialization_alias="None", validation_alias="None" - ) - true: bool = Field(serialization_alias="True", validation_alias="True") - type_: str = Field(serialization_alias="type", validation_alias="type") - match: str - - -class MyapiAdversarialPydanticReservedFields(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - model_config_: str = Field( - serialization_alias="model_config", validation_alias="model_config" - ) - model_fields_set_: str = Field( - serialization_alias="model_fields_set", validation_alias="model_fields_set" - ) - model_dump_json_: str = Field( - serialization_alias="model_dump_json", validation_alias="model_dump_json" - ) - - -class MyapiAdversarialShadowingFields(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - field: str - annotated: str - generic: str - base_model: str - - -class MyapiAdversarialTreeNode(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: str - children: list[myapi.adversarial.TreeNode] - parent: myapi.adversarial.TreeNode | None = None - - -class MyapiAdversarialWeirdDocstring(BaseModel): - """A docstring with \\"quotes\\" and \\'apostrophes\\' and a backslash: \\\\\\\\ -And a \\"\\"\\"triple quote\\"\\"\\" inside.""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: str - - -class MyapiAdversarialWrapper(BaseModel, Generic[T]): - """Each monomorphisation must produce a distinct Python class. - Shared-name collapse would mean the second usage\\'s fields would - silently shadow the first.""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - inner: T - - -class MyapiAdversarialKeywordVariantsClass(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - kind: Literal["class"] = Field(default="class", description="Discriminator field") - - -class MyapiAdversarialKeywordVariantsLambda(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - kind: Literal["lambda"] = Field(default="lambda", description="Discriminator field") - name: str - - -class MyapiAdversarialKeywordVariantsReturn(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - kind: Literal["return"] = Field(default="return", description="Discriminator field") - value: int - - -class MyapiAdversarialKeywordVariants(RootModel): - root: Annotated[ - Union[ - MyapiAdversarialKeywordVariantsClass, - MyapiAdversarialKeywordVariantsLambda, - MyapiAdversarialKeywordVariantsReturn, - ], - Field(discriminator="kind"), - ] - - -# Public aliases for this module -AdversarialRequest = MyapiAdversarialRequest -AdversarialResponse = MyapiAdversarialResponse -BaseModel = MyapiAdversarialBaseModel -DeepOption = MyapiAdversarialDeepOption -DefaultedField = MyapiAdversarialDefaultedField -EmptyStruct = MyapiAdversarialEmptyStruct -GenericTree = MyapiAdversarialGenericTree -IntKeyedMap = MyapiAdversarialIntKeyedMap -KeywordVariants = MyapiAdversarialKeywordVariants -KeywordVariantsClass = MyapiAdversarialKeywordVariantsClass -KeywordVariantsLambda = MyapiAdversarialKeywordVariantsLambda -KeywordVariantsReturn = MyapiAdversarialKeywordVariantsReturn -MutualA = MyapiAdversarialMutualA -MutualB = MyapiAdversarialMutualB -PyKeywordFields = MyapiAdversarialPyKeywordFields -PydanticReservedFields = MyapiAdversarialPydanticReservedFields -Request = MyapiAdversarialRequest -Response = MyapiAdversarialResponse -ShadowingFields = MyapiAdversarialShadowingFields -TreeNode = MyapiAdversarialTreeNode -WeirdDocstring = MyapiAdversarialWeirdDocstring -Wrapper = MyapiAdversarialWrapper - -__all__ = [ - "AdversarialRequest", - "AdversarialResponse", - "BaseModel", - "DeepOption", - "DefaultedField", - "EmptyStruct", - "GenericTree", - "IntKeyedMap", - "KeywordVariants", - "KeywordVariantsClass", - "KeywordVariantsLambda", - "KeywordVariantsReturn", - "MutualA", - "MutualB", - "MyapiAdversarialBaseModel", - "MyapiAdversarialDeepOption", - "MyapiAdversarialDefaultedField", - "MyapiAdversarialEmptyStruct", - "MyapiAdversarialGenericTree", - "MyapiAdversarialIntKeyedMap", - "MyapiAdversarialKeywordVariants", - "MyapiAdversarialKeywordVariantsClass", - "MyapiAdversarialKeywordVariantsLambda", - "MyapiAdversarialKeywordVariantsReturn", - "MyapiAdversarialMutualA", - "MyapiAdversarialMutualB", - "MyapiAdversarialPyKeywordFields", - "MyapiAdversarialPydanticReservedFields", - "MyapiAdversarialRequest", - "MyapiAdversarialResponse", - "MyapiAdversarialShadowingFields", - "MyapiAdversarialTreeNode", - "MyapiAdversarialWeirdDocstring", - "MyapiAdversarialWrapper", - "PyKeywordFields", - "PydanticReservedFields", - "Request", - "Response", - "ShadowingFields", - "TreeNode", - "WeirdDocstring", - "Wrapper", -] From 25e62c0d443e1324519e0d8880009f41539b1b82 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 12:22:05 +1200 Subject: [PATCH 06/17] ci: pin ruff to 0.15.8 in codegen smoke test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Python codegen shells out to `ruff format` for the generated client, so the in-tree snapshot has line-wrapping that depends on the ruff version. Without pinning, the smoke test's 'no codegen drift' diff check fails any time CI's ruff differs from the developer's local install — which is just version flap, not a real codegen change. --- .github/workflows/ci.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6115e273..96479c51 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -52,6 +52,13 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - uses: astral-sh/setup-uv@v3 + # The Python codegen shells out to `ruff format`; different ruff + # versions wrap long lines differently, so the snapshot-drift + # check below is meaningless unless CI and local devs use the + # same ruff version. Pin it explicitly. + - uses: astral-sh/ruff-action@v3 + with: + version: "0.15.8" - name: Regenerate demo Python client from schema run: | cargo run -p reflectapi-cli --quiet -- codegen \ From 688e9a3830b4e631ec6e067d9568ca7ecf9490cc Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 12:26:40 +1200 Subject: [PATCH 07/17] ci: stop the ruff-action from linting unrelated files in the smoke test The action runs `ruff check` over the whole repo by default and fails on pre-existing style issues in the runtime tests. We only want ruff on PATH so the codegen can call `ruff format` on its output; pass `args: --version` to short-circuit the lint phase (matches the Rust job's usage). --- .github/workflows/ci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 96479c51..527888f1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -59,6 +59,10 @@ jobs: - uses: astral-sh/ruff-action@v3 with: version: "0.15.8" + # `args` short-circuits the action's default `ruff check` — + # we only want ruff on PATH so the codegen can shell out to + # `ruff format`, not for the action itself to lint the repo. + args: "--version" - name: Regenerate demo Python client from schema run: | cargo run -p reflectapi-cli --quiet -- codegen \ From 6e50e234f451d0b2edaecc705af65056e3714b0b Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 13:07:07 +1200 Subject: [PATCH 08/17] codegen(python): don't emit dead classes for the reflectapi::Option ADT `reflectapi::Option` renders as `T | None` at every use site; the three states are carried by `model_fields_set` on the containing `ReflectapiPartialModel`. The schema still declares it as a tagged enum, so without this exclusion the codegen emits a parallel `ReflectapiOption` / `ReflectapiOptionSome` / `ReflectapiOptionNone` / `ReflectapiOptionUndefined` set of classes that nothing references. --- .../clients/python/api_client/__init__.py | 4 +--- .../clients/python/api_client/_client.py | 2 -- .../clients/python/api_client/_rebuild.py | 10 ---------- .../clients/python/api_client/generated.py | 13 ------------- reflectapi/src/codegen/python.rs | 10 +++++++++- 5 files changed, 10 insertions(+), 29 deletions(-) diff --git a/reflectapi-demo/clients/python/api_client/__init__.py b/reflectapi-demo/clients/python/api_client/__init__.py index fd260686..673ef347 100644 --- a/reflectapi-demo/clients/python/api_client/__init__.py +++ b/reflectapi-demo/clients/python/api_client/__init__.py @@ -10,10 +10,8 @@ from . import myapi -from . import reflectapi - from ._rebuild import rebuild_models as _rebuild_models _rebuild_models() -__all__ = ["AsyncClient", "Client", "myapi", "reflectapi"] +__all__ = ["AsyncClient", "Client", "myapi"] diff --git a/reflectapi-demo/clients/python/api_client/_client.py b/reflectapi-demo/clients/python/api_client/_client.py index f0f6a1c1..c39feef4 100644 --- a/reflectapi-demo/clients/python/api_client/_client.py +++ b/reflectapi-demo/clients/python/api_client/_client.py @@ -48,8 +48,6 @@ from . import myapi -from . import reflectapi - from ._rebuild import rebuild_models as _rebuild_models _rebuild_models() diff --git a/reflectapi-demo/clients/python/api_client/_rebuild.py b/reflectapi-demo/clients/python/api_client/_rebuild.py index 6480cb4b..5475e06e 100644 --- a/reflectapi-demo/clients/python/api_client/_rebuild.py +++ b/reflectapi-demo/clients/python/api_client/_rebuild.py @@ -89,15 +89,6 @@ MyapiProtoValidationErrorValidationAVariant, ) -from . import reflectapi - -from .reflectapi import ( - ReflectapiOption, - ReflectapiOptionNone, - ReflectapiOptionSome, - ReflectapiOptionUndefined, -) - def rebuild_models() -> None: myapi.myapi = myapi @@ -107,7 +98,6 @@ def rebuild_models() -> None: myapi.model.output.myapi = myapi myapi.order.myapi = myapi myapi.proto.myapi = myapi - reflectapi.reflectapi = reflectapi errors: list[str] = [] for _model in [ MyapiHealthCheckFail, diff --git a/reflectapi-demo/clients/python/api_client/generated.py b/reflectapi-demo/clients/python/api_client/generated.py index 10bd2372..38f043ca 100644 --- a/reflectapi-demo/clients/python/api_client/generated.py +++ b/reflectapi-demo/clients/python/api_client/generated.py @@ -93,15 +93,6 @@ MyapiProtoValidationErrorValidationAVariant, ) -from . import reflectapi - -from .reflectapi import ( - ReflectapiOption, - ReflectapiOptionNone, - ReflectapiOptionSome, - ReflectapiOptionUndefined, -) - from ._rebuild import rebuild_models as _rebuild_models _rebuild_models() @@ -162,8 +153,4 @@ "MyapiProtoValidationA", "MyapiProtoValidationError", "MyapiProtoValidationErrorValidationAVariant", - "ReflectapiOption", - "ReflectapiOptionNone", - "ReflectapiOptionSome", - "ReflectapiOptionUndefined", ] diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index 1e9678f5..b188dce0 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -1671,9 +1671,17 @@ fn build_python_generation( // Use optimized import generation instead of template generated_code.push(generate_optimized_imports(&imports)); - // Types that are provided by the runtime library and should not be generated + // Types that are mapped directly to Python primitives / built-ins + // and so don't need a generated class. let mut non_rendered_types = python_metadata.runtime_provided_types.clone(); non_rendered_types.insert("std::option::Option".to_string()); + // `reflectapi::Option` renders as `T | None` at every use site + // (its three states are carried by `model_fields_set` on the + // containing `ReflectapiPartialModel`). The schema still declares + // it as a tagged enum, so without this exclusion the codegen would + // emit a parallel ADT (`ReflectapiOption`, `ReflectapiOptionSome`, + // …) that nothing references. + non_rendered_types.insert("reflectapi::Option".to_string()); let class_names = build_python_class_name_map( semantic From 10ecb79188205fd3f4f8a8608cd5174e86d8fb1d Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 13:37:31 +1200 Subject: [PATCH 09/17] codegen(python): bind every top-level namespace into every other for rebuild Field annotations across sibling namespaces (e.g. `auth.AuthError` from inside `upload/__init__.py`) need the referenced namespace bound as a module-level global so Pydantic's `model_rebuild()` can resolve the name. The rebuild was previously only binding each namespace's own root, which worked for schemas with a single shared package prefix (the demo's `myapi..` shape) but left sibling references undefined for schemas where every namespace is its own top-level module (the common case for non-prefixed schemas like core-server). --- .../python/api_client/reflectapi/__init__.py | 110 ------------------ reflectapi/src/codegen/python.rs | 17 ++- 2 files changed, 15 insertions(+), 112 deletions(-) delete mode 100644 reflectapi-demo/clients/python/api_client/reflectapi/__init__.py diff --git a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py b/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py deleted file mode 100644 index fa1e6bd6..00000000 --- a/reflectapi-demo/clients/python/api_client/reflectapi/__init__.py +++ /dev/null @@ -1,110 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Demo application -This is a demo application -""" - -from __future__ import annotations - - -# Standard library imports -import warnings -from collections.abc import AsyncIterator, Iterator -from datetime import datetime -from enum import Enum -from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union - -# Third-party imports -from pydantic import ( - BaseModel, - ConfigDict, - Field, - RootModel, - model_serializer, - model_validator, -) - -# Runtime imports -from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration -from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible -from reflectapi_runtime import ReflectapiPartialModel -from reflectapi_runtime import ( - parse_externally_tagged as _parse_externally_tagged, - serialize_externally_tagged as _serialize_externally_tagged, -) - - -# Type variables for generic types - - -C = TypeVar("C") - -T = TypeVar("T") - - -# External type definitions -StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] -StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] -StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] -StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] - - -class ReflectapiOptionUndefined(BaseModel): - """The value is missing, i.e. undefined in JavaScript""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - -class ReflectapiOptionNone(BaseModel): - """The value is provided but set to none, i.e. null in JavaScript""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - -class ReflectapiOptionSome(BaseModel): - """The value is provided and set to some value""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: T | None = None - - -ReflectapiOption = Union[ - ReflectapiOptionUndefined, ReflectapiOptionNone, ReflectapiOptionSome -] -"""Undefinable Option type""" - - -# Public aliases for this module -Option = ReflectapiOption -OptionNone = ReflectapiOptionNone -OptionSome = ReflectapiOptionSome -OptionUndefined = ReflectapiOptionUndefined - -try: - from .._rebuild import rebuild_models as _rebuild_models - - _rebuild_models() -except ImportError: - pass - -__all__ = [ - "Option", - "OptionNone", - "OptionSome", - "OptionUndefined", - "ReflectapiOption", - "ReflectapiOptionNone", - "ReflectapiOptionSome", - "ReflectapiOptionUndefined", -] diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index b188dce0..f4b5ecef 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -2343,10 +2343,23 @@ fn render_rebuild_file( if generation.rebuild_models.is_empty() { body.push_str(" return\n"); } else { + // Every namespace module needs every *top-level* namespace + // bound as a global so forward-reference annotations like + // `auth.AuthError` resolve from inside (say) `upload.py`. + // Binding only each namespace's own root would handle paths + // like `myapi.model.X` (one shared root) but not the more + // common case where namespaces are siblings under the + // package and a type in one references a type in another. + let top_level_namespaces: BTreeSet = flat_imports + .keys() + .filter_map(|p| p.first().cloned()) + .map(|s| safe_python_module_segment(&s)) + .collect(); for ns_path in flat_imports.keys().filter(|path| !path.is_empty()) { let module = module_import_path(ns_path); - let root = safe_python_module_segment(&ns_path[0]); - body.push_str(&format!(" {module}.{root} = {root}\n")); + for top in &top_level_namespaces { + body.push_str(&format!(" {module}.{top} = {top}\n")); + } } if !root_type_names.is_empty() { for root in generation From 2905c89476599db504f8a81e97bf2579927ed5ba Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 13:43:10 +1200 Subject: [PATCH 10/17] codegen(python): destutter monomorphized arg names from namespaced types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `mangle_monomorphized_name` concatenated arg names with `::` replaced by `_`, so an arg of `uuid::Uuid` produced the suffix `uuid_Uuid` → `UuidUuid` after PascalCase, leaking the namespace cap into the class name (`IdentityDataUuidUuidX` instead of `IdentityDataUuidX`). Run the arg name through the same destutter the rest of the class-name pipeline uses. --- ...pi_demo__tests__serde__flatten_unit-5.snap | 28 ++++---- ..._generic_flatten_drops_inner_fields-5.snap | 22 +++---- ...eneric_flatten_enum_variant_typevar-5.snap | 64 +++++++++---------- ...rde__generic_flatten_leaf_collision-5.snap | 16 ++--- ...ests__serde__generic_flatten_nested-5.snap | 28 ++++---- ...ts__serde__generic_flatten_optional-5.snap | 28 ++++---- ..._generic_flatten_two_instantiations-5.snap | 22 +++---- ..._flatten_typevar_in_generic_context-5.snap | 30 ++++----- ...atten_typevar_nested_in_generic_arg-5.snap | 30 ++++----- reflectapi/src/codegen/python.rs | 14 +++- 10 files changed, 145 insertions(+), 137 deletions(-) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap index f111210f..1f0346d4 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__flatten_unit-5.snap @@ -32,7 +32,7 @@ class ReflectapiDemoTestsSerdeK(BaseModel): a: int -class ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTupleTuple0(BaseModel): +class ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTuple0(BaseModel): model_config = ConfigDict( extra="ignore", populate_by_name=True, protected_namespaces=() ) @@ -51,8 +51,8 @@ class reflectapi_demo: """Namespace for serde types.""" K = ReflectapiDemoTestsSerdeK - SReflectapiDemoTestsSerdeKStdTupleTuple0 = ( - ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTupleTuple0 + SReflectapiDemoTestsSerdeKStdTuple0 = ( + ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTuple0 ) @@ -65,18 +65,16 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0 + reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0 ] = None, - ) -> ApiResponse[ - reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0 - ]: + ) -> ApiResponse[reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0]: """ Args: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0]: Response containing reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0 data + ApiResponse[reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0]: Response containing reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0 data """ path = "/inout_test" @@ -85,7 +83,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0, + response_model=reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0, ) @@ -111,18 +109,16 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0 + reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0 ] = None, - ) -> ApiResponse[ - reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0 - ]: + ) -> ApiResponse[reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0]: """ Args: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0]: Response containing reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0 data + ApiResponse[reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0]: Response containing reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0 data """ path = "/inout_test" @@ -131,7 +127,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTupleTuple0, + response_model=reflectapi_demo.tests.serde.SReflectapiDemoTestsSerdeKStdTuple0, ) @@ -158,7 +154,7 @@ StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeK, - ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTupleTuple0, + ReflectapiDemoTestsSerdeSReflectapiDemoTestsSerdeKStdTuple0, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap index 8a253ce1..05f26394 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_drops_inner_fields-5.snap @@ -42,7 +42,7 @@ class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): inner_b: str -class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69( +class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99( BaseModel ): model_config = ConfigDict( @@ -66,7 +66,7 @@ class reflectapi_demo: TestFlattenIfElse = ReflectapiDemoTestsSerdeTestFlattenIfElse TestFlattenInner = ReflectapiDemoTestsSerdeTestFlattenInner - TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 + TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 class AsyncInoutClient: @@ -78,10 +78,10 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 ]: """ @@ -89,7 +89,7 @@ class AsyncInoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 data + ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 data """ path = "/inout_test" @@ -98,7 +98,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69, + response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99, ) @@ -124,10 +124,10 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 ]: """ @@ -135,7 +135,7 @@ class InoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 data + ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 data """ path = "/inout_test" @@ -144,7 +144,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69, + response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99, ) @@ -172,7 +172,7 @@ _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestFlattenInner, - ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69, + ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap index 8f058a6f..a1b8b7d7 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_enum_variant_typevar-5.snap @@ -52,7 +52,7 @@ class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): payload: str -class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4( +class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8( BaseModel ): model_config = ConfigDict( @@ -63,7 +63,7 @@ class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF6 payload: str -class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant( +class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant( BaseModel ): """Insert variant""" @@ -72,10 +72,10 @@ class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a extra="ignore", populate_by_name=True, protected_namespaces=() ) - field_0: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 + field_0: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 -class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant( +class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant( BaseModel ): """Remove variant""" @@ -84,20 +84,20 @@ class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a extra="ignore", populate_by_name=True, protected_namespaces=() ) - field_0: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 + field_0: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 # Externally tagged enum using RootModel -ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451Variants = Union[ - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant, - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant, +ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4Variants = Union[ + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant, + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant, Literal["Empty"], ] -class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451( +class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4( RootModel[ - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451Variants + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4Variants ] ): """Externally tagged enum""" @@ -109,22 +109,22 @@ class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a data, { "Insert": lambda v: ( - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant( + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant( field_0=v ) ), "Remove": lambda v: ( - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant( + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant( field_0=v ) ), "Empty": "_unit", }, ( - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant, - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant, + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant, + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant, ), - "ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451", + "ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4", ) @model_serializer @@ -135,20 +135,20 @@ class ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a "Insert": ( lambda r: isinstance( r, - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant, + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant, ), lambda r: {"Insert": r.field_0}, ), "Remove": ( lambda r: isinstance( r, - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant, + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant, ), lambda r: {"Remove": r.field_0}, ), "Empty": (lambda r: r == "Empty", lambda r: "Empty"), }, - "ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451", + "ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4", ) @@ -164,10 +164,10 @@ class reflectapi_demo: TestFlattenIdent = ReflectapiDemoTestsSerdeTestFlattenIdent TestFlattenIdentData = ReflectapiDemoTestsSerdeTestFlattenIdentData - TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 = ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 - TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant = ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451InsertVariant - TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant = ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451RemoveVariant - TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 = ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 + TestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 = ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 + TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant = ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4InsertVariant + TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant = ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4RemoveVariant + TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 = ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 class AsyncInoutClient: @@ -179,10 +179,10 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 + reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 + reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 ]: """ @@ -190,7 +190,7 @@ class AsyncInoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451]: Response containing reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 data + ApiResponse[reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4]: Response containing reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 data """ path = "/inout_test" @@ -199,7 +199,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451, + response_model=reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4, ) @@ -225,10 +225,10 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 + reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 + reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 ]: """ @@ -236,7 +236,7 @@ class InoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451]: Response containing reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451 data + ApiResponse[reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4]: Response containing reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4 data """ path = "/inout_test" @@ -245,7 +245,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451, + response_model=reflectapi_demo.tests.serde.TestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4, ) @@ -273,8 +273,8 @@ _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIdent, ReflectapiDemoTestsSerdeTestFlattenIdentData, - ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4, - ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl0a834451, + ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8, + ReflectapiDemoTestsSerdeTestIngestRelationReflectapiDemoTestsSerdeTestFl36e9c2f4, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap index 7475c896..0a569363 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_leaf_collision-5.snap @@ -38,11 +38,11 @@ class ReflectapiDemoTestsSerdeTestLeafCollisionPair(BaseModel): extra="ignore", populate_by_name=True, protected_namespaces=() ) - a: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb - b: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc + a: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeModuleAS0030345e + b: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeModuleBSD4368653 -class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb( +class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleAS0030345e( BaseModel ): model_config = ConfigDict( @@ -53,7 +53,7 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2 a_field: int -class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc( +class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBSD4368653( BaseModel ): model_config = ConfigDict( @@ -92,8 +92,8 @@ class reflectapi_demo: TestFlattenIfElse = ReflectapiDemoTestsSerdeTestFlattenIfElse TestLeafCollisionPair = ReflectapiDemoTestsSerdeTestLeafCollisionPair - TestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb - TestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc + TestUpdateOrElseReflectapiDemoTestsSerdeModuleAS0030345e = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleAS0030345e + TestUpdateOrElseReflectapiDemoTestsSerdeModuleBSD4368653 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBSD4368653 class module_a: """Namespace for module_a types.""" @@ -201,8 +201,8 @@ _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestLeafCollisionPair, - ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleASC2dd36fb, - ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBS8b56ffdc, + ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleAS0030345e, + ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeModuleBSD4368653, ReflectapiDemoTestsSerdeModuleASample, ReflectapiDemoTestsSerdeModuleBSample, ]: diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap index 11806ff8..3b34f8df 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_nested-5.snap @@ -49,7 +49,7 @@ class ReflectapiDemoTestsSerdeTestFlattenIfElse(BaseModel): code: int -class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4( +class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8( BaseModel ): model_config = ConfigDict( @@ -60,7 +60,7 @@ class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF6 payload: str -class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1( +class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8( BaseModel ): model_config = ConfigDict( @@ -85,8 +85,8 @@ class reflectapi_demo: TestFlattenIdent = ReflectapiDemoTestsSerdeTestFlattenIdent TestFlattenIdentData = ReflectapiDemoTestsSerdeTestFlattenIdentData TestFlattenIfElse = ReflectapiDemoTestsSerdeTestFlattenIfElse - TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 = ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 - TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 + TestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 = ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 + TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 class AsyncInoutClient: @@ -98,10 +98,10 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 ]: """ @@ -109,7 +109,7 @@ class AsyncInoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 data + ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 data """ path = "/inout_test" @@ -118,7 +118,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1, + response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8, ) @@ -144,10 +144,10 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 + reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 ]: """ @@ -155,7 +155,7 @@ class InoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1 data + ApiResponse[reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8]: Response containing reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8 data """ path = "/inout_test" @@ -164,7 +164,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1, + response_model=reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8, ) @@ -193,8 +193,8 @@ for _model in [ ReflectapiDemoTestsSerdeTestFlattenIdent, ReflectapiDemoTestsSerdeTestFlattenIdentData, ReflectapiDemoTestsSerdeTestFlattenIfElse, - ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4, - ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden9793afe1, + ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8, + ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestIden572a52e8, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap index 7c639546..f14bde13 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_optional-5.snap @@ -34,7 +34,7 @@ class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): inner_b: str -class ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde356d19e7( +class ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerdeEee9b477( BaseModel ): model_config = ConfigDict( @@ -46,7 +46,7 @@ class ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde35 inner_b: str | None = None -class ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30( +class ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd85639aef( BaseModel ): model_config = ConfigDict( @@ -73,12 +73,12 @@ class reflectapi_demo: class input: """Namespace for input types.""" - TestOptionalFlattenReflectapiDemoTestsSerde356d19e7 = ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde356d19e7 + TestOptionalFlattenReflectapiDemoTestsSerdeEee9b477 = ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerdeEee9b477 class output: """Namespace for output types.""" - TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30 = ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30 + TestOptionalFlattenReflectapiDemoTestsSerd85639aef = ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd85639aef class AsyncInoutClient: @@ -90,10 +90,10 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.input.TestOptionalFlattenReflectapiDemoTestsSerde356d19e7 + reflectapi_demo.tests.serde.input.TestOptionalFlattenReflectapiDemoTestsSerdeEee9b477 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30 + reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef ]: """ @@ -101,7 +101,7 @@ class AsyncInoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30]: Response containing reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30 data + ApiResponse[reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef]: Response containing reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef data """ path = "/inout_test" @@ -110,7 +110,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30, + response_model=reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef, ) @@ -136,10 +136,10 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.input.TestOptionalFlattenReflectapiDemoTestsSerde356d19e7 + reflectapi_demo.tests.serde.input.TestOptionalFlattenReflectapiDemoTestsSerdeEee9b477 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30 + reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef ]: """ @@ -147,7 +147,7 @@ class InoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30]: Response containing reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30 data + ApiResponse[reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef]: Response containing reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef data """ path = "/inout_test" @@ -156,7 +156,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30, + response_model=reflectapi_demo.tests.serde.output.TestOptionalFlattenReflectapiDemoTestsSerd85639aef, ) @@ -183,8 +183,8 @@ StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenInner, - ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerde356d19e7, - ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd6d4b6d30, + ReflectapiDemoTestsSerdeInputTestOptionalFlattenReflectapiDemoTestsSerdeEee9b477, + ReflectapiDemoTestsSerdeOutputTestOptionalFlattenReflectapiDemoTestsSerd85639aef, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap index d3a6cff5..f10330cd 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_two_instantiations-5.snap @@ -55,11 +55,11 @@ class ReflectapiDemoTestsSerdeTestTwoInstantiations(BaseModel): extra="ignore", populate_by_name=True, protected_namespaces=() ) - a: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 - b: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20 + a: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 + b: reflectapi_demo.tests.serde.TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat7b3f870b -class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69( +class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat7b3f870b( BaseModel ): model_config = ConfigDict( @@ -67,11 +67,10 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3a ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None - inner_a: int - inner_b: str + alt_x: bool -class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20( +class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99( BaseModel ): model_config = ConfigDict( @@ -79,7 +78,8 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd ) if_else: reflectapi_demo.tests.serde.TestFlattenIfElse | None - alt_x: bool + inner_a: int + inner_b: str # Namespace classes for dotted access to types @@ -96,8 +96,8 @@ class reflectapi_demo: TestFlattenInner = ReflectapiDemoTestsSerdeTestFlattenInner TestFlattenInnerAlt = ReflectapiDemoTestsSerdeTestFlattenInnerAlt TestTwoInstantiations = ReflectapiDemoTestsSerdeTestTwoInstantiations - TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69 - TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20 + TestUpdateOrElseReflectapiDemoTestsSerdeTestFlat7b3f870b = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat7b3f870b + TestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 = ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99 class AsyncInoutClient: @@ -197,8 +197,8 @@ for _model in [ ReflectapiDemoTestsSerdeTestFlattenInner, ReflectapiDemoTestsSerdeTestFlattenInnerAlt, ReflectapiDemoTestsSerdeTestTwoInstantiations, - ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat3ae7cb69, - ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatCd6baf20, + ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlat7b3f870b, + ReflectapiDemoTestsSerdeTestUpdateOrElseReflectapiDemoTestsSerdeTestFlatC43b4b99, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap index 01ef5a5e..57b7b9e2 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_in_generic_context-5.snap @@ -40,7 +40,7 @@ class ReflectapiDemoTestsSerdeTestFlattenIdentData(BaseModel): payload: str -class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4( +class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8( BaseModel ): model_config = ConfigDict( @@ -51,14 +51,14 @@ class ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF6 payload: str -class ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66( +class ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061( BaseModel ): model_config = ConfigDict( extra="ignore", populate_by_name=True, protected_namespaces=() ) - body: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 + body: reflectapi_demo.tests.serde.TestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 extra: bool @@ -74,8 +74,8 @@ class reflectapi_demo: TestFlattenIdent = ReflectapiDemoTestsSerdeTestFlattenIdent TestFlattenIdentData = ReflectapiDemoTestsSerdeTestFlattenIdentData - TestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 = ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4 - TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 = ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 + TestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 = ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8 + TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 = ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 class AsyncInoutClient: @@ -87,10 +87,10 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 + reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 + reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 ]: """ @@ -98,7 +98,7 @@ class AsyncInoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66]: Response containing reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 data + ApiResponse[reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061]: Response containing reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 data """ path = "/inout_test" @@ -107,7 +107,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66, + response_model=reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061, ) @@ -133,10 +133,10 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 + reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 + reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 ]: """ @@ -144,7 +144,7 @@ class InoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66]: Response containing reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66 data + ApiResponse[reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061]: Response containing reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061 data """ path = "/inout_test" @@ -153,7 +153,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66, + response_model=reflectapi_demo.tests.serde.TestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061, ) @@ -181,8 +181,8 @@ _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIdent, ReflectapiDemoTestsSerdeTestFlattenIdentData, - ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlatF60133e4, - ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestF02f28d66, + ReflectapiDemoTestsSerdeTestIdentityDataReflectapiDemoTestsSerdeTestFlat59f317e8, + ReflectapiDemoTestsSerdeTestWithMarkedInnerReflectapiDemoTestsSerdeTestFF163d061, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap index d9069681..2d31dc12 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__generic_flatten_typevar_nested_in_generic_arg-5.snap @@ -42,7 +42,7 @@ class ReflectapiDemoTestsSerdeTestFlattenInner(BaseModel): inner_b: str -class ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e( +class ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionReflectapiDemoTestsSerd2c778819( BaseModel ): model_config = ConfigDict( @@ -54,14 +54,14 @@ class ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9 inner_b: str -class ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b( +class ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff( BaseModel ): model_config = ConfigDict( extra="ignore", populate_by_name=True, protected_namespaces=() ) - body: reflectapi_demo.tests.serde.TestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e + body: reflectapi_demo.tests.serde.TestUpdateOrElseStdOptionReflectapiDemoTestsSerd2c778819 extra: int @@ -77,8 +77,8 @@ class reflectapi_demo: TestFlattenIfElse = ReflectapiDemoTestsSerdeTestFlattenIfElse TestFlattenInner = ReflectapiDemoTestsSerdeTestFlattenInner - TestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e = ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e - TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b = ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b + TestUpdateOrElseStdOptionReflectapiDemoTestsSerd2c778819 = ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionReflectapiDemoTestsSerd2c778819 + TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff = ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff class AsyncInoutClient: @@ -90,10 +90,10 @@ class AsyncInoutClient: async def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b + reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b + reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff ]: """ @@ -101,7 +101,7 @@ class AsyncInoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b]: Response containing reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b data + ApiResponse[reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff]: Response containing reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff data """ path = "/inout_test" @@ -110,7 +110,7 @@ class AsyncInoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b, + response_model=reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff, ) @@ -136,10 +136,10 @@ class InoutClient: def test( self, data: Optional[ - reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b + reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff ] = None, ) -> ApiResponse[ - reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b + reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff ]: """ @@ -147,7 +147,7 @@ class InoutClient: data: Request data for the test operation. Returns: - ApiResponse[reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b]: Response containing reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b data + ApiResponse[reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff]: Response containing reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff data """ path = "/inout_test" @@ -156,7 +156,7 @@ class InoutClient: path, params=params if params else None, json_model=data, - response_model=reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b, + response_model=reflectapi_demo.tests.serde.TestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff, ) @@ -184,8 +184,8 @@ _rebuild_errors: list[str] = [] for _model in [ ReflectapiDemoTestsSerdeTestFlattenIfElse, ReflectapiDemoTestsSerdeTestFlattenInner, - ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionOptionReflectapiDemoTesB9dceb2e, - ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes59f9972b, + ReflectapiDemoTestsSerdeTestUpdateOrElseStdOptionReflectapiDemoTestsSerd2c778819, + ReflectapiDemoTestsSerdeTestWrapperWithNestedTypevarArgReflectapiDemoTes807013ff, ]: if not hasattr(_model, "model_rebuild"): continue diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index f4b5ecef..36f8a0eb 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -7106,7 +7106,19 @@ fn debug_assert_monomorphization_invariants( /// references stay consistent. fn mangle_monomorphized_name(struct_name: &str, args: &[TypeReference]) -> String { fn arg_suffix(arg: &TypeReference) -> String { - let base = arg.name.replace("::", "_"); + // Run the arg name through the same destutter the rest of + // the class-name pipeline uses. Without this, a type like + // `uuid::Uuid` produces the suffix `uuid_Uuid`, which then + // PascalCases to `UuidUuid` in the final class name — making + // `IdentityData` emit + // `IdentityDataUuidUuidX` instead of `IdentityDataUuidX`. + let base = if arg.name.contains("::") { + let parts: Vec<&str> = arg.name.split("::").collect(); + let pascal_parts: Vec = parts.iter().map(|p| to_pascal_case(p)).collect(); + maybe_destutter_pascal_parts(&pascal_parts) + } else { + arg.name.replace("::", "_") + }; if arg.arguments.is_empty() { base } else { From c001e4cb43fb1f8464c9418b2e47b7a13e5952ea Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 13:46:04 +1200 Subject: [PATCH 11/17] codegen(python): rename fields that shadow deprecated BaseModel methods Pydantic v2 emits a UserWarning when a field name shadows one of the deprecated v1 BaseModel methods (`schema`, `json`, `dict`, `parse_obj`, etc.). Extend the existing exact-match rename to cover them so generated classes import without warnings. --- ...ctapi_demo__tests__serde__external_impls-5.snap | 2 +- reflectapi/src/codegen/python.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap index c14ed57d..b53530e2 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__serde__external_impls-5.snap @@ -32,7 +32,7 @@ class ReflectapiDemoTestsSerdeTest(BaseModel): index_map: dict[int, int] index_set: list[str] url: str - json: Any + json_: Any = Field(serialization_alias="json", validation_alias="json") # Namespace classes for dotted access to types diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index 36f8a0eb..4bcd1868 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -4714,6 +4714,20 @@ fn is_pydantic_method_name(name: &str) -> bool { | "model_fields_set" | "model_extra" | "model_computed_fields" + // Deprecated-but-still-present BaseModel methods that + // Pydantic v2 warns about when a field shadows them. + | "schema" + | "schema_json" + | "json" + | "dict" + | "parse_obj" + | "parse_raw" + | "parse_file" + | "from_orm" + | "construct" + | "copy" + | "validate" + | "update_forward_refs" ) } From ab53177c624d5cada9814aca17160101fa98cd82 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 14:17:20 +1200 Subject: [PATCH 12/17] codegen(python): emit string literals with the quote style that needs fewest escapes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field descriptions used to be pre-escaped for double-quoted wrapping (\" everywhere), then ruff would reflow them to single quotes — leaving literal backslashes in the formatted output (`'... \"NZ\" ...'`). Pick the wrapping quote per literal: prefer double quotes, fall back to single when the content has " but no ', and only escape when both appear. --- reflectapi/src/codegen/python.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/reflectapi/src/codegen/python.rs b/reflectapi/src/codegen/python.rs index 4bcd1868..46de93ec 100644 --- a/reflectapi/src/codegen/python.rs +++ b/reflectapi/src/codegen/python.rs @@ -12,13 +12,23 @@ fn sanitize_for_docstring(text: &str) -> String { text.replace('\\', "\\\\").replace("\"\"\"", "\\\"\\\"\\\"") } -/// Sanitize text for inclusion in a Python double-quoted string literal. -/// Escapes backslashes, double quotes, and replaces newlines with \n. -fn sanitize_for_string_literal(text: &str) -> String { - text.replace('\\', "\\\\") - .replace('"', "\\\"") +/// Format ``text`` as a Python string literal, choosing single or +/// double quotes to minimise the number of escapes (matching +/// ``repr()``'s convention). Backslashes, newlines, and carriage +/// returns are always escaped; the quote character only gets escaped +/// when both forms appear in the input. +fn python_string_literal(text: &str) -> String { + let escaped_common = text + .replace('\\', "\\\\") .replace('\n', "\\n") - .replace('\r', "\\r") + .replace('\r', "\\r"); + let has_double = escaped_common.contains('"'); + let has_single = escaped_common.contains('\''); + let (quote, body) = match (has_double, has_single) { + (true, false) => ('\'', escaped_common), + (false, _) | (true, true) => ('"', escaped_common.replace('"', "\\\"")), + }; + format!("{quote}{body}{quote}") } /// Configuration for Python client generation @@ -5432,7 +5442,7 @@ pub mod templates { .description .as_ref() .filter(|d| !d.is_empty() && !d.starts_with("(flattened")) - .map(|d| super::sanitize_for_string_literal(d)); + .map(|d| super::python_string_literal(d)); let has_field_args = desc.is_some() || field.alias.is_some() || (field.optional && field.alias.is_none()); @@ -5451,7 +5461,10 @@ pub mod templates { args.push(format!("validation_alias='{alias}'")); } if let Some(ref d) = desc { - args.push(format!("description=\"{d}\"")); + // `d` is already a complete Python string + // literal (quotes included) from + // `python_string_literal`. + args.push(format!("description={d}")); } write!(s, "{}", args.join(", ")).unwrap(); write!(s, ")").unwrap(); From c0c8f0d7926d1dc2637d21313cefb2de6a33d382 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 14:26:19 +1200 Subject: [PATCH 13/17] derive: read `#[doc]` literals via LitStr::value() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `parse_doc_attributes` was converting the doc literal via `to_token_stream().to_string()`, which produced the Rust source representation of the string — preserving every `\"` escape from the original source. Doc comments containing literal double quotes therefore landed in the schema as bytes `\"` instead of `"`, and that quoted form propagated into Python field descriptions. Use `Lit::Str::value()` to read the actual string content. --- .../api_client/myapi/coverage/__init__.py | 10 +++++++-- .../clients/rust/generated/src/generated.rs | 8 +++++-- .../clients/typescript/generated.ts | 12 ++++++++-- reflectapi-demo/openapi.json | 12 +++++++++- reflectapi-demo/reflectapi.json | 18 ++++++++++++++- reflectapi-demo/src/lib.rs | 4 ++++ ...ctapi_demo__tests__write_openapi_spec.snap | 12 +++++++++- reflectapi-derive/src/parser.rs | 22 +++++++++++-------- 8 files changed, 80 insertions(+), 18 deletions(-) diff --git a/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py index a1bb3d11..5bc89634 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/coverage/__init__.py @@ -212,14 +212,20 @@ class MyapiCoverageTreeNode(BaseModel): class MyapiCoverageWeirdDocstring(BaseModel): - """A docstring with \\"quotes\\" and \\'apostrophes\\' and a backslash: \\\\\\\\ -And a \\"\\"\\"triple quote\\"\\"\\" inside.""" + """A docstring with "quotes" and 'apostrophes' and a backslash: \\\\ +And a \"\"\"triple quote\"\"\" inside.""" model_config = ConfigDict( extra="ignore", populate_by_name=True, protected_namespaces=() ) value: str + mixed_quotes: str = Field( + description="Field description with \"double quotes\" and 'single quotes'." + ) + doubles_only: str = Field( + description='Field description with only "double quotes" — should use single-quoted Python literal.' + ) class MyapiCoverageWrapper(BaseModel, Generic[T]): diff --git a/reflectapi-demo/clients/rust/generated/src/generated.rs b/reflectapi-demo/clients/rust/generated/src/generated.rs index 36d99376..2cd7a822 100644 --- a/reflectapi-demo/clients/rust/generated/src/generated.rs +++ b/reflectapi-demo/clients/rust/generated/src/generated.rs @@ -346,11 +346,15 @@ pub mod types { pub type UserId = u64; - /// A docstring with \"quotes\" and \'apostrophes\' and a backslash: \\\\ - /// And a \"\"\"triple quote\"\"\" inside. + /// A docstring with "quotes" and 'apostrophes' and a backslash: \\ + /// And a """triple quote""" inside. #[derive(Debug, serde::Serialize)] pub struct WeirdDocstring { pub value: std::string::String, + /// Field description with "double quotes" and 'single quotes'. + pub mixed_quotes: std::string::String, + /// Field description with only "double quotes" — should use single-quoted Python literal. + pub doubles_only: std::string::String, } #[derive(Debug, serde::Serialize)] diff --git a/reflectapi-demo/clients/typescript/generated.ts b/reflectapi-demo/clients/typescript/generated.ts index 2f50da44..b4521b54 100644 --- a/reflectapi-demo/clients/typescript/generated.ts +++ b/reflectapi-demo/clients/typescript/generated.ts @@ -851,11 +851,19 @@ export namespace myapi { export type UserId = number /* u64 */; /** - * A docstring with \"quotes\" and \'apostrophes\' and a backslash: \\\\ - * And a \"\"\"triple quote\"\"\" inside. + * A docstring with "quotes" and 'apostrophes' and a backslash: \\ + * And a """triple quote""" inside. */ export interface WeirdDocstring { value: string; + /** + * Field description with "double quotes" and 'single quotes'. + */ + mixed_quotes: string; + /** + * Field description with only "double quotes" — should use single-quoted Python literal. + */ + doubles_only: string; } export interface Wrapper { diff --git a/reflectapi-demo/openapi.json b/reflectapi-demo/openapi.json index 7ffa715e..47775c63 100644 --- a/reflectapi-demo/openapi.json +++ b/reflectapi-demo/openapi.json @@ -945,13 +945,23 @@ } }, "myapi.coverage.WeirdDocstring": { - "description": "A docstring with \\\"quotes\\\" and \\'apostrophes\\' and a backslash: \\\\\\\\\nAnd a \\\"\\\"\\\"triple quote\\\"\\\"\\\" inside.", + "description": "A docstring with \"quotes\" and 'apostrophes' and a backslash: \\\\\nAnd a \"\"\"triple quote\"\"\" inside.", "type": "object", "title": "myapi.coverage.WeirdDocstring", "required": [ + "doubles_only", + "mixed_quotes", "value" ], "properties": { + "doubles_only": { + "description": "Field description with only \"double quotes\" — should use single-quoted Python literal.", + "$ref": "#/components/schemas/std.string.String" + }, + "mixed_quotes": { + "description": "Field description with \"double quotes\" and 'single quotes'.", + "$ref": "#/components/schemas/std.string.String" + }, "value": { "$ref": "#/components/schemas/std.string.String" } diff --git a/reflectapi-demo/reflectapi.json b/reflectapi-demo/reflectapi.json index 5200cea0..bc9d67c9 100644 --- a/reflectapi-demo/reflectapi.json +++ b/reflectapi-demo/reflectapi.json @@ -872,7 +872,7 @@ { "kind": "struct", "name": "myapi::coverage::WeirdDocstring", - "description": "A docstring with \\\"quotes\\\" and \\'apostrophes\\' and a backslash: \\\\\\\\\nAnd a \\\"\\\"\\\"triple quote\\\"\\\"\\\" inside.", + "description": "A docstring with \"quotes\" and 'apostrophes' and a backslash: \\\\\nAnd a \"\"\"triple quote\"\"\" inside.", "fields": { "named": [ { @@ -881,6 +881,22 @@ "name": "std::string::String" }, "required": true + }, + { + "name": "mixed_quotes", + "description": "Field description with \"double quotes\" and 'single quotes'.", + "type": { + "name": "std::string::String" + }, + "required": true + }, + { + "name": "doubles_only", + "description": "Field description with only \"double quotes\" — should use single-quoted Python literal.", + "type": { + "name": "std::string::String" + }, + "required": true } ] } diff --git a/reflectapi-demo/src/lib.rs b/reflectapi-demo/src/lib.rs index 228b8030..473df9b1 100644 --- a/reflectapi-demo/src/lib.rs +++ b/reflectapi-demo/src/lib.rs @@ -332,6 +332,10 @@ mod coverage { )] pub struct WeirdDocstring { pub value: String, + /// Field description with "double quotes" and 'single quotes'. + pub mixed_quotes: String, + /// Field description with only "double quotes" — should use single-quoted Python literal. + pub doubles_only: String, } // ---- Type name that shadows a Pydantic-imported symbol ---- diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap index 73bc353e..59db1954 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap @@ -949,13 +949,23 @@ expression: s } }, "myapi.coverage.WeirdDocstring": { - "description": "A docstring with \\\"quotes\\\" and \\'apostrophes\\' and a backslash: \\\\\\\\\nAnd a \\\"\\\"\\\"triple quote\\\"\\\"\\\" inside.", + "description": "A docstring with \"quotes\" and 'apostrophes' and a backslash: \\\\\nAnd a \"\"\"triple quote\"\"\" inside.", "type": "object", "title": "myapi.coverage.WeirdDocstring", "required": [ + "doubles_only", + "mixed_quotes", "value" ], "properties": { + "doubles_only": { + "description": "Field description with only \"double quotes\" — should use single-quoted Python literal.", + "$ref": "#/components/schemas/std.string.String" + }, + "mixed_quotes": { + "description": "Field description with \"double quotes\" and 'single quotes'.", + "$ref": "#/components/schemas/std.string.String" + }, "value": { "$ref": "#/components/schemas/std.string.String" } diff --git a/reflectapi-derive/src/parser.rs b/reflectapi-derive/src/parser.rs index de89219c..9b7922fd 100644 --- a/reflectapi-derive/src/parser.rs +++ b/reflectapi-derive/src/parser.rs @@ -146,16 +146,20 @@ pub(crate) fn parse_doc_attributes(attrs: &Vec) -> String { continue; } + // Read the actual string value of the `#[doc = "..."]` literal. + // The previous implementation went through `to_token_stream()`, + // which produced the *Rust source* representation (with the + // surrounding quotes and any embedded `\"` escapes still in + // place), so a doc comment containing literal double quotes + // landed in the schema as `\"`-prefixed bytes. if let syn::Meta::NameValue(meta) = &attr.meta { - result.push( - meta.value - .to_token_stream() - .to_string() - .as_str() - .trim_matches('"') - .trim() - .to_string(), - ); + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(litstr), + .. + }) = &meta.value + { + result.push(litstr.value().trim().to_string()); + } } } result.join("\n") From aa4b580bf539eb2edca1d936f8b973908a810324 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 15:29:58 +1200 Subject: [PATCH 14/17] codegen(ts) + runtime(py): flush SSE parsers on stream close MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TypeScript `__EventSourceParserStream` had no `flush()` method, so a server that closed the connection immediately after the final `data:` line — without the spec's trailing blank-line terminator — silently dropped the last event. Add `flush() { parser.feed('\n\n') }` to the template so the buffered event dispatches on stream close. The Python runtime already handled the symmetric case via `feed_eof()` in both `parse_sse` and `aparse_sse`. Add explicit tests for the async parser eof-flush and for the end-to-end streaming client receiving a server response that closes mid-event. --- .../clients/typescript/generated.ts | 8 +++ reflectapi-python-runtime/tests/test_sse.py | 69 +++++++++++++++++++ reflectapi/src/codegen/lib.ts | 6 ++ 3 files changed, 83 insertions(+) diff --git a/reflectapi-demo/clients/typescript/generated.ts b/reflectapi-demo/clients/typescript/generated.ts index b4521b54..77f7ac10 100644 --- a/reflectapi-demo/clients/typescript/generated.ts +++ b/reflectapi-demo/clients/typescript/generated.ts @@ -626,6 +626,14 @@ class __EventSourceParserStream extends TransformStream< transform(chunk) { parser.feed(chunk); }, + // SSE events are terminated by a blank line. If the upstream + // closes after the final `data:` line without a trailing + // separator, the parser still has that event buffered — feed + // the terminator on stream close so it gets dispatched + // instead of silently dropped. + flush() { + parser.feed("\n\n"); + }, }); } } diff --git a/reflectapi-python-runtime/tests/test_sse.py b/reflectapi-python-runtime/tests/test_sse.py index db9e0bb0..050a4640 100644 --- a/reflectapi-python-runtime/tests/test_sse.py +++ b/reflectapi-python-runtime/tests/test_sse.py @@ -95,6 +95,31 @@ async def gen(): assert [e.data for e in events] == ["1", "2"] +@pytest.mark.asyncio +async def test_aparse_flushes_trailing_event_on_eof() -> None: + """The async parser must surface a final event whose terminator was + cut off by stream close.""" + + async def gen(): + yield "data: tail" + + events = [e async for e in aparse_sse(gen())] + assert events == [SseEvent(data="tail")] + + +@pytest.mark.asyncio +async def test_aparse_flushes_multiline_event_on_eof() -> None: + """Multi-line events also need to dispatch on eof when the trailing + blank line is missing.""" + + async def gen(): + for line in ["event: ping", "data: hi", "data: there"]: + yield line + + events = [e async for e in aparse_sse(gen())] + assert events == [SseEvent(data="hi\nthere", event="ping")] + + # --------------------------------------------------------------------------- # Full streaming flow against an in-process httpx MockTransport # --------------------------------------------------------------------------- @@ -270,6 +295,50 @@ def handler(request: httpx.Request) -> httpx.Response: await client.aclose() +# --------------------------------------------------------------------------- +# End-of-stream flush (server closes without a trailing blank line). +# A symmetric bug bit the TypeScript SSE template — the parser stream +# had no flush() so the final buffered event was dropped. Python's +# `feed_eof()` covers the same case; these tests pin the end-to-end +# behaviour through the streaming client. +# --------------------------------------------------------------------------- + + +def _sse_body_no_trailing_blank(items: list[dict]) -> bytes: + """SSE body whose final event is *not* followed by the spec's + blank-line terminator — simulates a server that closes the + connection immediately after the last event.""" + joined = "\n\n".join(f"data: {json.dumps(it)}" for it in items) + return joined.encode() # no trailing \n\n + + +def test_sync_streaming_emits_final_event_when_server_closes_mid_event() -> None: + pets = [{"name": "fido", "weight": 1.5}, {"name": "rex", "weight": 4.0}] + + def handler(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=_sse_body_no_trailing_blank(pets)) + + client = _make_sync_client(httpx.MockTransport(handler)) + received = list(client.stream_pets()) + assert received == [_Pet(**p) for p in pets] + client.close() + + +@pytest.mark.asyncio +async def test_async_streaming_emits_final_event_when_server_closes_mid_event() -> None: + pets = [{"name": "fido", "weight": 1.5}, {"name": "rex", "weight": 4.0}] + + def handler(request: httpx.Request) -> httpx.Response: + return httpx.Response(200, content=_sse_body_no_trailing_blank(pets)) + + client = _make_async_client(httpx.MockTransport(handler)) + received: list[_Pet] = [] + async for pet in client.stream_pets(): + received.append(pet) + assert received == [_Pet(**p) for p in pets] + await client.aclose() + + # --------------------------------------------------------------------------- # Test harness wiring (asyncio mode) # --------------------------------------------------------------------------- diff --git a/reflectapi/src/codegen/lib.ts b/reflectapi/src/codegen/lib.ts index 862e3375..eb1e3220 100644 --- a/reflectapi/src/codegen/lib.ts +++ b/reflectapi/src/codegen/lib.ts @@ -585,6 +585,12 @@ class __EventSourceParserStream extends TransformStream Date: Wed, 13 May 2026 15:59:04 +1200 Subject: [PATCH 15/17] codegen(python): preserve exclude-none for plain models + move coverage fixtures out of the demo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Restore the historical wire format for plain Pydantic request models: `model_dump_json(by_alias=True, exclude_none=True)` for non-partial models so unset optional fields stay absent on the wire. `ReflectapiPartialModel` still uses no exclude — explicit `None` encodes the protocol's "field present, value cleared" state and must reach the wire. Same fix applied in `client.py` and `streaming.py`. - Move codegen coverage fixtures (PyKeywordFields, recursive types, Pydantic-reserved field names, etc.) out of the public demo schema into the integration test at `reflectapi-demo/tests/codegen_coverage.rs`. The demo crate's `builder()` now only registers pet-themed routes. The integration test builds a private schema, runs Python codegen to `target/codegen-coverage-client/`, and CI imports the result so strict `model_rebuild()` still catches dangling-reference bugs. - Add four runtime tests pinning the partial-vs-plain wire-format contract. --- .github/workflows/ci.yaml | 19 +- .../clients/python/api_client/_client.py | 92 -- .../clients/python/api_client/_rebuild.py | 59 +- .../clients/python/api_client/generated.py | 60 +- .../python/api_client/myapi/__init__.py | 39 +- .../python/api_client/myapi/model/__init__.py | 4 - .../api_client/myapi/model/input/__init__.py | 4 - .../api_client/myapi/model/output/__init__.py | 4 - .../python/api_client/myapi/proto/__init__.py | 4 - .../clients/rust/generated/src/generated.rs | 210 ---- .../clients/typescript/generated.ts | 230 ---- reflectapi-demo/openapi.json | 667 ------------ reflectapi-demo/reflectapi.json | 998 +----------------- reflectapi-demo/src/lib.rs | 294 ------ ...ctapi_demo__tests__write_openapi_spec.snap | 667 ------------ .../codegen_coverage_client/__init__.py | 17 + .../codegen_coverage_client/_client.py | 167 +++ .../codegen_coverage_client/_rebuild.py | 94 ++ .../codegen_coverage/__init__.py | 81 ++ .../codegen_coverage/coverage/__init__.py | 329 ++++++ .../codegen_coverage/order/__init__.py | 95 ++ .../codegen_coverage_client/generated.py | 88 ++ reflectapi-demo/tests/codegen_coverage.rs | 331 ++++++ .../src/reflectapi_runtime/client.py | 50 +- .../src/reflectapi_runtime/streaming.py | 19 +- .../tests/test_codegen_regressions.py | 69 ++ 26 files changed, 1357 insertions(+), 3334 deletions(-) create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py create mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py create mode 100644 reflectapi-demo/tests/codegen_coverage.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 527888f1..eb54d874 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -72,5 +72,22 @@ jobs: - name: Import demo client (triggers strict model_rebuild) working-directory: reflectapi-demo/clients/python run: uv run python -c "import api_client; print('demo client import OK')" - - name: Snapshot is committed (no drift) + - name: Demo snapshot is committed (no drift) run: git diff --exit-code reflectapi-demo/clients/python/api_client/ + # Codegen coverage fixtures live in the integration test at + # `reflectapi-demo/tests/codegen_coverage.rs` (not in the + # public demo schema). The test writes a Python client to + # `target/codegen-coverage-client/`; the next step imports it + # so the strict `_rebuild_models()` raises on any dangling + # reference. + - name: Generate codegen coverage client + run: cargo test -p reflectapi-demo --test codegen_coverage + - name: Import codegen coverage client + working-directory: reflectapi-demo/clients/python + run: | + uv run python -c " + import sys, pathlib + sys.path.insert(0, str(pathlib.Path('../../target/codegen-coverage-client').resolve())) + import codegen_coverage_client + print('coverage client import OK') + " diff --git a/reflectapi-demo/clients/python/api_client/_client.py b/reflectapi-demo/clients/python/api_client/_client.py index c39feef4..36357941 100644 --- a/reflectapi-demo/clients/python/api_client/_client.py +++ b/reflectapi-demo/clients/python/api_client/_client.py @@ -28,9 +28,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -41,8 +39,6 @@ # Type variables for generic types -C = TypeVar("C") - T = TypeVar("T") @@ -276,50 +272,6 @@ def __init__( self.pets = AsyncPetsClient(self) - async def codegen_coverage( - self, - data: Optional[myapi.coverage.CoverageRequest] = None, - ) -> ApiResponse[myapi.coverage.CoverageResponse]: - """Coverage fixtures for codegen edge cases - - Args: - data: Request data for the codegen_coverage operation. - - Returns: - ApiResponse[myapi.coverage.CoverageResponse]: Response containing myapi.coverage.CoverageResponse data - """ - path = "/codegen-coverage" - - params: dict[str, Any] = {} - return await self._make_request( - path, - params=params if params else None, - json_model=data, - response_model=myapi.coverage.CoverageResponse, - ) - - async def codegen_order_coverage( - self, - data: Optional[myapi.OrderCoverageRequest] = None, - ) -> ApiResponse[myapi.OrderCoverageResponse]: - """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering - - Args: - data: Request data for the codegen_order_coverage operation. - - Returns: - ApiResponse[myapi.OrderCoverageResponse]: Response containing myapi.OrderCoverageResponse data - """ - path = "/codegen-order-coverage" - - params: dict[str, Any] = {} - return await self._make_request( - path, - params=params if params else None, - json_model=data, - response_model=myapi.OrderCoverageResponse, - ) - class HealthClient: """Synchronous client for health operations.""" @@ -543,47 +495,3 @@ def __init__( self.health = HealthClient(self) self.pets = PetsClient(self) - - def codegen_coverage( - self, - data: Optional[myapi.coverage.CoverageRequest] = None, - ) -> ApiResponse[myapi.coverage.CoverageResponse]: - """Coverage fixtures for codegen edge cases - - Args: - data: Request data for the codegen_coverage operation. - - Returns: - ApiResponse[myapi.coverage.CoverageResponse]: Response containing myapi.coverage.CoverageResponse data - """ - path = "/codegen-coverage" - - params: dict[str, Any] = {} - return self._make_request( - path, - params=params if params else None, - json_model=data, - response_model=myapi.coverage.CoverageResponse, - ) - - def codegen_order_coverage( - self, - data: Optional[myapi.OrderCoverageRequest] = None, - ) -> ApiResponse[myapi.OrderCoverageResponse]: - """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering - - Args: - data: Request data for the codegen_order_coverage operation. - - Returns: - ApiResponse[myapi.OrderCoverageResponse]: Response containing myapi.OrderCoverageResponse data - """ - path = "/codegen-order-coverage" - - params: dict[str, Any] = {} - return self._make_request( - path, - params=params if params else None, - json_model=data, - response_model=myapi.OrderCoverageResponse, - ) diff --git a/reflectapi-demo/clients/python/api_client/_rebuild.py b/reflectapi-demo/clients/python/api_client/_rebuild.py index 5475e06e..33866c66 100644 --- a/reflectapi-demo/clients/python/api_client/_rebuild.py +++ b/reflectapi-demo/clients/python/api_client/_rebuild.py @@ -11,36 +11,7 @@ from . import myapi -from .myapi import ( - MyapiHealthCheckFail, - MyapiOrderCoverageRequest, - MyapiOrderCoverageResponse, -) - -from . import myapi - -from .myapi.coverage import ( - MyapiCoverageBaseModel, - MyapiCoverageDeepOption, - MyapiCoverageDefaultedField, - MyapiCoverageEmptyStruct, - MyapiCoverageGenericTree, - MyapiCoverageIntKeyedMap, - MyapiCoverageKeywordVariants, - MyapiCoverageKeywordVariantsClass, - MyapiCoverageKeywordVariantsLambda, - MyapiCoverageKeywordVariantsReturn, - MyapiCoverageMutualA, - MyapiCoverageMutualB, - MyapiCoveragePyKeywordFields, - MyapiCoveragePydanticReservedFields, - MyapiCoverageRequest, - MyapiCoverageResponse, - MyapiCoverageShadowingFields, - MyapiCoverageTreeNode, - MyapiCoverageWeirdDocstring, - MyapiCoverageWrapper, -) +from .myapi import MyapiHealthCheckFail from . import myapi @@ -64,10 +35,6 @@ from . import myapi -from .myapi.order import MyapiOrderInsertData, MyapiOrderPolicy, MyapiOrderRateLimit - -from . import myapi - from .myapi.proto import ( MyapiProtoHeaders, MyapiProtoInternalError, @@ -92,41 +59,17 @@ def rebuild_models() -> None: myapi.myapi = myapi - myapi.coverage.myapi = myapi myapi.model.myapi = myapi myapi.model.input.myapi = myapi myapi.model.output.myapi = myapi - myapi.order.myapi = myapi myapi.proto.myapi = myapi errors: list[str] = [] for _model in [ MyapiHealthCheckFail, - MyapiOrderCoverageRequest, - MyapiOrderCoverageResponse, - MyapiCoverageBaseModel, - MyapiCoverageRequest, - MyapiCoverageResponse, - MyapiCoverageDeepOption, - MyapiCoverageDefaultedField, - MyapiCoverageEmptyStruct, - MyapiCoverageGenericTree, - MyapiCoverageIntKeyedMap, - MyapiCoverageKeywordVariants, - MyapiCoverageMutualA, - MyapiCoverageMutualB, - MyapiCoveragePyKeywordFields, - MyapiCoveragePydanticReservedFields, - MyapiCoverageShadowingFields, - MyapiCoverageTreeNode, - MyapiCoverageWeirdDocstring, - MyapiCoverageWrapper, MyapiModelBehavior, MyapiModelKind, MyapiModelInputPet, MyapiModelOutputPet, - MyapiOrderInsertData, - MyapiOrderPolicy, - MyapiOrderRateLimit, MyapiProtoHeaders, MyapiProtoInternalError, MyapiProtoPaginated, diff --git a/reflectapi-demo/clients/python/api_client/generated.py b/reflectapi-demo/clients/python/api_client/generated.py index 38f043ca..6a9f72e2 100644 --- a/reflectapi-demo/clients/python/api_client/generated.py +++ b/reflectapi-demo/clients/python/api_client/generated.py @@ -15,36 +15,7 @@ from . import myapi -from .myapi import ( - MyapiHealthCheckFail, - MyapiOrderCoverageRequest, - MyapiOrderCoverageResponse, -) - -from . import myapi - -from .myapi.coverage import ( - MyapiCoverageBaseModel, - MyapiCoverageDeepOption, - MyapiCoverageDefaultedField, - MyapiCoverageEmptyStruct, - MyapiCoverageGenericTree, - MyapiCoverageIntKeyedMap, - MyapiCoverageKeywordVariants, - MyapiCoverageKeywordVariantsClass, - MyapiCoverageKeywordVariantsLambda, - MyapiCoverageKeywordVariantsReturn, - MyapiCoverageMutualA, - MyapiCoverageMutualB, - MyapiCoveragePyKeywordFields, - MyapiCoveragePydanticReservedFields, - MyapiCoverageRequest, - MyapiCoverageResponse, - MyapiCoverageShadowingFields, - MyapiCoverageTreeNode, - MyapiCoverageWeirdDocstring, - MyapiCoverageWrapper, -) +from .myapi import MyapiHealthCheckFail from . import myapi @@ -68,10 +39,6 @@ from . import myapi -from .myapi.order import MyapiOrderInsertData, MyapiOrderPolicy, MyapiOrderRateLimit - -from . import myapi - from .myapi.proto import ( MyapiProtoHeaders, MyapiProtoInternalError, @@ -100,26 +67,6 @@ __all__ = [ "AsyncClient", "Client", - "MyapiCoverageBaseModel", - "MyapiCoverageDeepOption", - "MyapiCoverageDefaultedField", - "MyapiCoverageEmptyStruct", - "MyapiCoverageGenericTree", - "MyapiCoverageIntKeyedMap", - "MyapiCoverageKeywordVariants", - "MyapiCoverageKeywordVariantsClass", - "MyapiCoverageKeywordVariantsLambda", - "MyapiCoverageKeywordVariantsReturn", - "MyapiCoverageMutualA", - "MyapiCoverageMutualB", - "MyapiCoveragePyKeywordFields", - "MyapiCoveragePydanticReservedFields", - "MyapiCoverageRequest", - "MyapiCoverageResponse", - "MyapiCoverageShadowingFields", - "MyapiCoverageTreeNode", - "MyapiCoverageWeirdDocstring", - "MyapiCoverageWrapper", "MyapiHealthCheckFail", "MyapiModelBehavior", "MyapiModelBehaviorAggressiveVariant", @@ -130,11 +77,6 @@ "MyapiModelKindCat", "MyapiModelKindDog", "MyapiModelOutputPet", - "MyapiOrderCoverageRequest", - "MyapiOrderCoverageResponse", - "MyapiOrderInsertData", - "MyapiOrderPolicy", - "MyapiOrderRateLimit", "MyapiProtoHeaders", "MyapiProtoInternalError", "MyapiProtoPaginated", diff --git a/reflectapi-demo/clients/python/api_client/myapi/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/__init__.py index b55fc7a6..3b0419d0 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/__init__.py @@ -28,9 +28,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -41,8 +39,6 @@ # Type variables for generic types -C = TypeVar("C") - T = TypeVar("T") @@ -59,32 +55,10 @@ class MyapiHealthCheckFail(BaseModel): ) -class MyapiOrderCoverageRequest(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - order: myapi.order.OrderInsertData - rate_limit: myapi.order.RateLimit - policy: myapi.order.Policy[str, int] - - -class MyapiOrderCoverageResponse(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - ok: bool - - # Public aliases for this module HealthCheckFail = MyapiHealthCheckFail -OrderCoverageRequest = MyapiOrderCoverageRequest -OrderCoverageResponse = MyapiOrderCoverageResponse -from . import coverage from . import model -from . import order from . import proto try: @@ -94,15 +68,4 @@ class MyapiOrderCoverageResponse(BaseModel): except ImportError: pass -__all__ = [ - "HealthCheckFail", - "MyapiHealthCheckFail", - "MyapiOrderCoverageRequest", - "MyapiOrderCoverageResponse", - "OrderCoverageRequest", - "OrderCoverageResponse", - "coverage", - "model", - "order", - "proto", -] +__all__ = ["HealthCheckFail", "MyapiHealthCheckFail", "model", "proto"] diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py index d2cf03c0..20a86b12 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/__init__.py @@ -28,9 +28,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -41,8 +39,6 @@ # Type variables for generic types -C = TypeVar("C") - T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py index 451525ab..0aa3cde7 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/input/__init__.py @@ -28,9 +28,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -41,8 +39,6 @@ # Type variables for generic types -C = TypeVar("C") - T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py index d759ffec..9545b2ce 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/model/output/__init__.py @@ -28,9 +28,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -41,8 +39,6 @@ # Type variables for generic types -C = TypeVar("C") - T = TypeVar("T") diff --git a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py index a4b89e9f..b64d0a67 100644 --- a/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py +++ b/reflectapi-demo/clients/python/api_client/myapi/proto/__init__.py @@ -28,9 +28,7 @@ # Runtime imports from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible from reflectapi_runtime import ReflectapiPartialModel from reflectapi_runtime import ( parse_externally_tagged as _parse_externally_tagged, @@ -41,8 +39,6 @@ # Type variables for generic types -C = TypeVar("C") - T = TypeVar("T") diff --git a/reflectapi-demo/clients/rust/generated/src/generated.rs b/reflectapi-demo/clients/rust/generated/src/generated.rs index 2cd7a822..cbb53d4b 100644 --- a/reflectapi-demo/clients/rust/generated/src/generated.rs +++ b/reflectapi-demo/clients/rust/generated/src/generated.rs @@ -27,31 +27,6 @@ pub mod interface { client, } } - /// Coverage fixtures for namespace/tuple/Duration/PhantomData rendering - #[tracing::instrument(name = "/codegen-order-coverage", skip(self, headers))] - pub async fn codegen_order_coverage( - &self, - input: super::types::myapi::OrderCoverageRequest, - headers: reflectapi::Empty, - ) -> Result< - super::types::myapi::OrderCoverageResponse, - reflectapi::rt::Error, - > { - reflectapi::rt::__request_impl(&self.client, "/codegen-order-coverage", input, headers) - .await - } - /// Coverage fixtures for codegen edge cases - #[tracing::instrument(name = "/codegen-coverage", skip(self, headers))] - pub async fn codegen_coverage( - &self, - input: super::types::myapi::coverage::CoverageRequest, - headers: reflectapi::Empty, - ) -> Result< - super::types::myapi::coverage::CoverageResponse, - reflectapi::rt::Error, - > { - reflectapi::rt::__request_impl(&self.client, "/codegen-coverage", input, headers).await - } } #[cfg(feature = "reqwest")] @@ -208,160 +183,6 @@ pub mod types { impl std::error::Error for HealthCheckFail {} - #[derive(Debug, serde::Serialize)] - pub struct OrderCoverageRequest { - pub order: super::myapi::order::OrderInsertData, - pub rate_limit: super::myapi::order::RateLimit, - pub policy: super::myapi::order::Policy, - } - - #[derive(Debug, serde::Deserialize)] - pub struct OrderCoverageResponse { - pub ok: bool, - } - - pub mod coverage { - - #[derive(Debug, serde::Serialize)] - pub struct BaseModel { - pub label: std::string::String, - } - - #[derive(Debug, serde::Serialize)] - pub struct CoverageRequest { - pub keywords: super::super::myapi::coverage::PyKeywordFields, - pub reserved: super::super::myapi::coverage::PydanticReservedFields, - pub tree: super::super::myapi::coverage::TreeNode, - pub mutual: super::super::myapi::coverage::MutualA, - pub generic_tree: super::super::myapi::coverage::GenericTree, - pub keyword_variants: super::super::myapi::coverage::KeywordVariants, - pub int_keyed: super::super::myapi::coverage::IntKeyedMap, - pub user_id: super::super::myapi::coverage::UserId, - pub deep_option: super::super::myapi::coverage::DeepOption, - pub shadowing: super::super::myapi::coverage::ShadowingFields, - pub empty: super::super::myapi::coverage::EmptyStruct, - pub weird_doc: super::super::myapi::coverage::WeirdDocstring, - pub shadow_base_model: super::super::myapi::coverage::BaseModel, - pub wrapper_int: super::super::myapi::coverage::Wrapper, - pub wrapper_str: super::super::myapi::coverage::Wrapper, - pub defaulted: super::super::myapi::coverage::DefaultedField, - } - - #[derive(Debug, serde::Deserialize)] - pub struct CoverageResponse { - pub ok: bool, - } - - #[derive(Debug, serde::Serialize)] - pub struct DeepOption { - pub maybe_maybe: reflectapi::Option>, - } - - #[derive(Debug, serde::Serialize)] - pub struct DefaultedField { - #[serde(default = "Default::default")] - pub count: u32, - } - - #[derive(Debug, serde::Serialize)] - pub struct EmptyStruct {} - - #[derive(Debug, serde::Serialize)] - pub struct GenericTree { - pub value: T, - pub children: std::vec::Vec>, - } - - #[derive(Debug, serde::Serialize)] - pub struct IntKeyedMap { - pub by_id: std::collections::HashMap, - pub uuid_keyed: std::collections::HashMap, - } - - #[derive(Debug, serde::Serialize)] - #[serde(tag = "kind")] - pub enum KeywordVariants { - #[serde(rename = "class")] - Class, - #[serde(rename = "lambda")] - Lambda { name: std::string::String }, - #[serde(rename = "return")] - Return { value: i32 }, - } - - #[derive(Debug, serde::Serialize)] - pub struct MutualA { - pub name: std::string::String, - pub b: std::option::Option>, - } - - #[derive(Debug, serde::Serialize)] - pub struct MutualB { - pub name: std::string::String, - pub a: std::option::Option>, - } - - #[derive(Debug, serde::Serialize)] - pub struct PyKeywordFields { - pub class: std::string::String, - pub from: u32, - pub import: bool, - pub lambda: i64, - #[serde(rename = "return")] - pub return_: std::vec::Vec, - #[serde(rename = "yield")] - pub yield_: std::option::Option, - #[serde(rename = "None")] - pub none: std::option::Option, - #[serde(rename = "True")] - pub true_: bool, - #[serde(rename = "type")] - pub type_: std::string::String, - #[serde(rename = "match")] - pub match_: std::string::String, - } - - #[derive(Debug, serde::Serialize)] - pub struct PydanticReservedFields { - pub model_config: std::string::String, - pub model_fields_set: std::string::String, - pub model_dump_json: std::string::String, - } - - #[derive(Debug, serde::Serialize)] - pub struct ShadowingFields { - pub field: std::string::String, - pub annotated: std::string::String, - pub generic: std::string::String, - pub base_model: std::string::String, - } - - #[derive(Debug, serde::Serialize)] - pub struct TreeNode { - pub value: std::string::String, - pub children: std::vec::Vec, - pub parent: - std::option::Option>, - } - - pub type UserId = u64; - - /// A docstring with "quotes" and 'apostrophes' and a backslash: \\ - /// And a """triple quote""" inside. - #[derive(Debug, serde::Serialize)] - pub struct WeirdDocstring { - pub value: std::string::String, - /// Field description with "double quotes" and 'single quotes'. - pub mixed_quotes: std::string::String, - /// Field description with only "double quotes" — should use single-quoted Python literal. - pub doubles_only: std::string::String, - } - - #[derive(Debug, serde::Serialize)] - pub struct Wrapper { - pub inner: T, - } - } pub mod model { #[derive(Debug, serde::Deserialize, serde::Serialize)] @@ -456,37 +277,6 @@ pub mod types { } } } - pub mod order { - - /// A struct whose name begins with the parent namespace cap. - /// Exercises the namespace-alias path for both the stripped - /// (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) - /// forms. - #[derive(Debug, serde::Serialize)] - pub struct OrderInsertData { - pub identity: std::string::String, - /// Exercises the `tuple[A, B]` rendering for `(A, B)`. - pub alternative_part_number: - std::option::Option<(std::string::String, std::string::String)>, - } - - /// Exercises `PhantomData` elision (the field carries no wire - /// data and must not appear in the Python model). - #[derive(Debug, serde::Serialize)] - pub struct Policy { - pub name: std::string::String, - pub _context_marker: std::marker::PhantomData, - pub _output_marker: std::marker::PhantomData, - } - - /// Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire - /// adapter. - #[derive(Debug, serde::Serialize)] - pub struct RateLimit { - pub retry_after: std::time::Duration, - pub max_wait: std::option::Option, - } - } pub mod proto { #[derive(Debug, serde::Serialize)] diff --git a/reflectapi-demo/clients/typescript/generated.ts b/reflectapi-demo/clients/typescript/generated.ts index 77f7ac10..d34af02c 100644 --- a/reflectapi-demo/clients/typescript/generated.ts +++ b/reflectapi-demo/clients/typescript/generated.ts @@ -642,22 +642,6 @@ class __EventSourceParserStream extends TransformStream< export namespace __definition { export interface Interface { - /** - * Coverage fixtures for namespace/tuple/Duration/PhantomData rendering - */ - codegen_order_coverage: ( - input: myapi.OrderCoverageRequest, - headers: {}, - options?: RequestOptions, - ) => AsyncResult; - /** - * Coverage fixtures for codegen edge cases - */ - codegen_coverage: ( - input: myapi.coverage.CoverageRequest, - headers: {}, - options?: RequestOptions, - ) => AsyncResult; health: HealthInterface; pets: PetsInterface; } @@ -745,140 +729,6 @@ export namespace __definition { export namespace myapi { export interface HealthCheckFail {} - export interface OrderCoverageRequest { - order: myapi.order.OrderInsertData; - rate_limit: myapi.order.RateLimit; - policy: myapi.order.Policy; - } - - export interface OrderCoverageResponse { - ok: boolean; - } - - export namespace coverage { - export interface BaseModel { - label: string; - } - - export interface CoverageRequest { - keywords: myapi.coverage.PyKeywordFields; - reserved: myapi.coverage.PydanticReservedFields; - tree: myapi.coverage.TreeNode; - mutual: myapi.coverage.MutualA; - generic_tree: myapi.coverage.GenericTree; - keyword_variants: myapi.coverage.KeywordVariants; - int_keyed: myapi.coverage.IntKeyedMap; - user_id: myapi.coverage.UserId; - deep_option: myapi.coverage.DeepOption; - shadowing: myapi.coverage.ShadowingFields; - empty: myapi.coverage.EmptyStruct; - weird_doc: myapi.coverage.WeirdDocstring; - shadow_base_model: myapi.coverage.BaseModel; - wrapper_int: myapi.coverage.Wrapper; - wrapper_str: myapi.coverage.Wrapper; - defaulted: myapi.coverage.DefaultedField; - } - - export interface CoverageResponse { - ok: boolean; - } - - export interface DeepOption { - maybe_maybe: (string | null) | null | undefined; - } - - export interface DefaultedField { - count?: number /* u32 */; - } - - export interface EmptyStruct {} - - export interface GenericTree { - value: T; - children: Array>; - } - - export interface IntKeyedMap { - by_id: Record; - uuid_keyed: Record; - } - - export type KeywordVariants = - | { kind: "class" } - | { - kind: "lambda"; - name: string; - } - | { - kind: "return"; - value: number /* i32 */; - }; - - export interface MutualA { - name: string; - b: myapi.coverage.MutualB | null; - } - - export interface MutualB { - name: string; - a: myapi.coverage.MutualA | null; - } - - export interface PyKeywordFields { - class: string; - from: number /* u32 */; - import: boolean; - lambda: number /* i64 */; - return: Array; - yield: string | null; - None: number /* i32 */ | null; - True: boolean; - type: string; - match: string; - } - - export interface PydanticReservedFields { - model_config: string; - model_fields_set: string; - model_dump_json: string; - } - - export interface ShadowingFields { - field: string; - annotated: string; - generic: string; - base_model: string; - } - - export interface TreeNode { - value: string; - children: Array; - parent: myapi.coverage.TreeNode | null; - } - - export type UserId = number /* u64 */; - - /** - * A docstring with "quotes" and 'apostrophes' and a backslash: \\ - * And a """triple quote""" inside. - */ - export interface WeirdDocstring { - value: string; - /** - * Field description with "double quotes" and 'single quotes'. - */ - mixed_quotes: string; - /** - * Field description with only "double quotes" — should use single-quoted Python literal. - */ - doubles_only: string; - } - - export interface Wrapper { - inner: T; - } - } - export namespace model { export type Behavior = | "Calm" @@ -981,41 +831,6 @@ export namespace myapi { } } - export namespace order { - /** - * A struct whose name begins with the parent namespace cap. - * Exercises the namespace-alias path for both the stripped - * (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) - * forms. - */ - export interface OrderInsertData { - identity: string; - /** - * Exercises the `tuple[A, B]` rendering for `(A, B)`. - */ - alternative_part_number: [string, string] | null; - } - - /** - * Exercises `PhantomData` elision (the field carries no wire - * data and must not appear in the Python model). - */ - export interface Policy { - name: string; - _context_marker: undefined | C /* phantom data */; - _output_marker: undefined | T /* phantom data */; - } - - /** - * Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire - * adapter. - */ - export interface RateLimit { - retry_after: std.time.Duration; - max_wait: std.time.Duration | null; - } - } - export namespace proto { export interface Headers { /** @@ -1112,23 +927,6 @@ export namespace reflectapi { * Struct object with no fields */ export interface Empty {} - - /** - * Error object which is expected to be never returned - */ - export interface Infallible {} -} - -export namespace std { - export namespace time { - /** - * Time duration type - */ - export interface Duration { - secs: number /* u64 */; - nanos: number /* u32 */; - } - } } namespace __implementation { @@ -1139,8 +937,6 @@ namespace __implementation { typeof base === "string" ? new ClientInstance(base) : base; return { impl: { - codegen_order_coverage: codegen_order_coverage(client_instance), - codegen_coverage: codegen_coverage(client_instance), health: { check: health__check(client_instance), }, @@ -1260,30 +1056,4 @@ namespace __implementation { myapi.proto.UnauthorizedError >(client, "/pets.cdc-events", input, headers, options); } - function codegen_order_coverage(client: Client) { - return ( - input: myapi.OrderCoverageRequest, - headers: {}, - options?: RequestOptions, - ) => - __request< - myapi.OrderCoverageRequest, - {}, - myapi.OrderCoverageResponse, - {} - >(client, "/codegen-order-coverage", input, headers, options); - } - function codegen_coverage(client: Client) { - return ( - input: myapi.coverage.CoverageRequest, - headers: {}, - options?: RequestOptions, - ) => - __request< - myapi.coverage.CoverageRequest, - {}, - myapi.coverage.CoverageResponse, - {} - >(client, "/codegen-coverage", input, headers, options); - } } diff --git a/reflectapi-demo/openapi.json b/reflectapi-demo/openapi.json index 47775c63..3a31792d 100644 --- a/reflectapi-demo/openapi.json +++ b/reflectapi-demo/openapi.json @@ -6,64 +6,6 @@ "version": "1.0.0" }, "paths": { - "/codegen-coverage": { - "description": "Coverage fixtures for codegen edge cases", - "post": { - "operationId": "codegen-coverage", - "description": "Coverage fixtures for codegen edge cases", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.coverage.CoverageRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.coverage.CoverageResponse" - } - } - } - } - } - } - }, - "/codegen-order-coverage": { - "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", - "post": { - "operationId": "codegen-order-coverage", - "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.OrderCoverageRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.OrderCoverageResponse" - } - } - } - } - } - } - }, "/pets.cdc-events": { "description": "Stream of change data capture events for pets", "post": { @@ -439,534 +381,10 @@ }, "components": { "schemas": { - "bool": { - "description": "Boolean value", - "type": "boolean" - }, "f64": { "description": "64-bit floating point number", "type": "number" }, - "i32": { - "description": "32-bit signed integer", - "type": "integer" - }, - "i64": { - "description": "64-bit signed integer", - "type": "integer" - }, - "myapi.OrderCoverageRequest": { - "type": "object", - "title": "myapi.OrderCoverageRequest", - "required": [ - "order", - "policy", - "rate_limit" - ], - "properties": { - "order": { - "$ref": "#/components/schemas/myapi.order.OrderInsertData" - }, - "policy": { - "description": "Exercises `PhantomData` elision (the field carries no wire\ndata and must not appear in the Python model).", - "type": "object", - "title": "myapi.order.Policy", - "required": [ - "_context_marker", - "_output_marker", - "name" - ], - "properties": { - "_context_marker": { - "description": "Zero-sized phantom data", - "type": "null" - }, - "_output_marker": { - "description": "Zero-sized phantom data", - "type": "null" - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "rate_limit": { - "$ref": "#/components/schemas/myapi.order.RateLimit" - } - } - }, - "myapi.OrderCoverageResponse": { - "type": "object", - "title": "myapi.OrderCoverageResponse", - "required": [ - "ok" - ], - "properties": { - "ok": { - "$ref": "#/components/schemas/bool" - } - } - }, - "myapi.coverage.BaseModel": { - "type": "object", - "title": "myapi.coverage.BaseModel", - "required": [ - "label" - ], - "properties": { - "label": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.CoverageRequest": { - "type": "object", - "title": "myapi.coverage.CoverageRequest", - "required": [ - "deep_option", - "defaulted", - "empty", - "generic_tree", - "int_keyed", - "keyword_variants", - "keywords", - "mutual", - "reserved", - "shadow_base_model", - "shadowing", - "tree", - "user_id", - "weird_doc", - "wrapper_int", - "wrapper_str" - ], - "properties": { - "deep_option": { - "$ref": "#/components/schemas/myapi.coverage.DeepOption" - }, - "defaulted": { - "$ref": "#/components/schemas/myapi.coverage.DefaultedField" - }, - "empty": { - "$ref": "#/components/schemas/myapi.coverage.EmptyStruct" - }, - "generic_tree": { - "type": "object", - "title": "myapi.coverage.GenericTree", - "required": [ - "children", - "value" - ], - "properties": { - "children": { - "description": "Expandable array type", - "type": "array", - "items": { - "$ref": "#/components/schemas/myapi.coverage.GenericTree_i32_" - } - }, - "value": { - "$ref": "#/components/schemas/i32" - } - } - }, - "int_keyed": { - "$ref": "#/components/schemas/myapi.coverage.IntKeyedMap" - }, - "keyword_variants": { - "$ref": "#/components/schemas/myapi.coverage.KeywordVariants" - }, - "keywords": { - "$ref": "#/components/schemas/myapi.coverage.PyKeywordFields" - }, - "mutual": { - "$ref": "#/components/schemas/myapi.coverage.MutualA" - }, - "reserved": { - "$ref": "#/components/schemas/myapi.coverage.PydanticReservedFields" - }, - "shadow_base_model": { - "$ref": "#/components/schemas/myapi.coverage.BaseModel" - }, - "shadowing": { - "$ref": "#/components/schemas/myapi.coverage.ShadowingFields" - }, - "tree": { - "$ref": "#/components/schemas/myapi.coverage.TreeNode" - }, - "user_id": { - "$ref": "#/components/schemas/u64" - }, - "weird_doc": { - "$ref": "#/components/schemas/myapi.coverage.WeirdDocstring" - }, - "wrapper_int": { - "type": "object", - "title": "myapi.coverage.Wrapper", - "required": [ - "inner" - ], - "properties": { - "inner": { - "$ref": "#/components/schemas/i32" - } - } - }, - "wrapper_str": { - "type": "object", - "title": "myapi.coverage.Wrapper", - "required": [ - "inner" - ], - "properties": { - "inner": { - "$ref": "#/components/schemas/std.string.String" - } - } - } - } - }, - "myapi.coverage.CoverageResponse": { - "type": "object", - "title": "myapi.coverage.CoverageResponse", - "required": [ - "ok" - ], - "properties": { - "ok": { - "$ref": "#/components/schemas/bool" - } - } - }, - "myapi.coverage.DeepOption": { - "type": "object", - "title": "myapi.coverage.DeepOption", - "required": [ - "maybe_maybe" - ], - "properties": { - "maybe_maybe": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/std.string.String" - } - ] - } - ] - } - } - }, - "myapi.coverage.DefaultedField": { - "type": "object", - "title": "myapi.coverage.DefaultedField", - "properties": { - "count": { - "$ref": "#/components/schemas/u32" - } - } - }, - "myapi.coverage.EmptyStruct": { - "type": "object", - "title": "myapi.coverage.EmptyStruct", - "properties": {} - }, - "myapi.coverage.IntKeyedMap": { - "type": "object", - "title": "myapi.coverage.IntKeyedMap", - "required": [ - "by_id", - "uuid_keyed" - ], - "properties": { - "by_id": { - "description": "Key-value map type", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/std.string.String" - } - }, - "uuid_keyed": { - "description": "Key-value map type", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/std.string.String" - } - } - } - }, - "myapi.coverage.KeywordVariants": { - "oneOf": [ - { - "type": "object", - "title": "class", - "required": [ - "kind" - ], - "properties": { - "kind": { - "const": "class" - } - } - }, - { - "type": "object", - "title": "lambda", - "required": [ - "kind", - "name" - ], - "properties": { - "kind": { - "const": "lambda" - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - { - "type": "object", - "title": "return", - "required": [ - "kind", - "value" - ], - "properties": { - "kind": { - "const": "return" - }, - "value": { - "$ref": "#/components/schemas/i32" - } - } - } - ] - }, - "myapi.coverage.MutualA": { - "type": "object", - "title": "myapi.coverage.MutualA", - "required": [ - "b", - "name" - ], - "properties": { - "b": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/myapi.coverage.MutualB" - } - ] - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.MutualB": { - "type": "object", - "title": "myapi.coverage.MutualB", - "required": [ - "a", - "name" - ], - "properties": { - "a": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/myapi.coverage.MutualA" - } - ] - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.PyKeywordFields": { - "type": "object", - "title": "myapi.coverage.PyKeywordFields", - "required": [ - "None", - "True", - "class", - "from", - "import", - "lambda", - "match", - "return", - "type", - "yield" - ], - "properties": { - "None": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/i32" - } - ] - }, - "True": { - "$ref": "#/components/schemas/bool" - }, - "class": { - "$ref": "#/components/schemas/std.string.String" - }, - "from": { - "$ref": "#/components/schemas/u32" - }, - "import": { - "$ref": "#/components/schemas/bool" - }, - "lambda": { - "$ref": "#/components/schemas/i64" - }, - "match": { - "$ref": "#/components/schemas/std.string.String" - }, - "return": { - "description": "Expandable array type", - "type": "array", - "items": { - "$ref": "#/components/schemas/u8" - } - }, - "type": { - "$ref": "#/components/schemas/std.string.String" - }, - "yield": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/std.string.String" - } - ] - } - } - }, - "myapi.coverage.PydanticReservedFields": { - "type": "object", - "title": "myapi.coverage.PydanticReservedFields", - "required": [ - "model_config", - "model_dump_json", - "model_fields_set" - ], - "properties": { - "model_config": { - "$ref": "#/components/schemas/std.string.String" - }, - "model_dump_json": { - "$ref": "#/components/schemas/std.string.String" - }, - "model_fields_set": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.ShadowingFields": { - "type": "object", - "title": "myapi.coverage.ShadowingFields", - "required": [ - "annotated", - "base_model", - "field", - "generic" - ], - "properties": { - "annotated": { - "$ref": "#/components/schemas/std.string.String" - }, - "base_model": { - "$ref": "#/components/schemas/std.string.String" - }, - "field": { - "$ref": "#/components/schemas/std.string.String" - }, - "generic": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.TreeNode": { - "type": "object", - "title": "myapi.coverage.TreeNode", - "required": [ - "children", - "parent", - "value" - ], - "properties": { - "children": { - "description": "Expandable array type", - "type": "array", - "items": { - "$ref": "#/components/schemas/myapi.coverage.TreeNode" - } - }, - "parent": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/myapi.coverage.TreeNode" - } - ] - }, - "value": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.WeirdDocstring": { - "description": "A docstring with \"quotes\" and 'apostrophes' and a backslash: \\\\\nAnd a \"\"\"triple quote\"\"\" inside.", - "type": "object", - "title": "myapi.coverage.WeirdDocstring", - "required": [ - "doubles_only", - "mixed_quotes", - "value" - ], - "properties": { - "doubles_only": { - "description": "Field description with only \"double quotes\" — should use single-quoted Python literal.", - "$ref": "#/components/schemas/std.string.String" - }, - "mixed_quotes": { - "description": "Field description with \"double quotes\" and 'single quotes'.", - "$ref": "#/components/schemas/std.string.String" - }, - "value": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, "myapi.model.Behavior": { "oneOf": [ { @@ -1159,66 +577,6 @@ } } }, - "myapi.order.OrderInsertData": { - "description": "A struct whose name begins with the parent namespace cap.\nExercises the namespace-alias path for both the stripped\n(`order.InsertData`) and Rust-leaf (`order.OrderInsertData`)\nforms.", - "type": "object", - "title": "myapi.order.OrderInsertData", - "required": [ - "alternative_part_number", - "identity" - ], - "properties": { - "alternative_part_number": { - "description": "Exercises the `tuple[A, B]` rendering for `(A, B)`.", - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "description": "Tuple holding 2 elements", - "type": "array", - "prefixItems": [ - { - "$ref": "#/components/schemas/std.string.String" - }, - { - "$ref": "#/components/schemas/std.string.String" - } - ] - } - ] - }, - "identity": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.order.RateLimit": { - "description": "Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire\nadapter.", - "type": "object", - "title": "myapi.order.RateLimit", - "required": [ - "max_wait", - "retry_after" - ], - "properties": { - "max_wait": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/std.time.Duration" - } - ] - }, - "retry_after": { - "$ref": "#/components/schemas/std.time.Duration" - } - } - }, "myapi.proto.InternalError": { "type": "object", "title": "myapi.proto.InternalError", @@ -1484,35 +842,10 @@ "description": "UTF-8 encoded string", "type": "string" }, - "std.time.Duration": { - "description": "Time duration type", - "type": "object", - "title": "std.time.Duration", - "required": [ - "nanos", - "secs" - ], - "properties": { - "nanos": { - "$ref": "#/components/schemas/u32" - }, - "secs": { - "$ref": "#/components/schemas/u64" - } - } - }, "std.tuple.Tuple0": { "description": "Unit type", "type": "null" }, - "u32": { - "description": "32-bit unsigned integer", - "type": "integer" - }, - "u64": { - "description": "64-bit unsigned integer", - "type": "integer" - }, "u8": { "description": "8-bit unsigned integer", "type": "integer" diff --git a/reflectapi-demo/reflectapi.json b/reflectapi-demo/reflectapi.json index bc9d67c9..f014da8b 100644 --- a/reflectapi-demo/reflectapi.json +++ b/reflectapi-demo/reflectapi.json @@ -167,760 +167,28 @@ "msgpack" ], "readonly": true - }, - { - "name": "codegen-order-coverage", - "path": "", - "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", - "input_type": { - "name": "myapi::OrderCoverageRequest" - }, - "output_kind": "complete", - "output_type": { - "name": "myapi::OrderCoverageResponse" - }, - "serialization": [ - "json", - "msgpack" - ] - }, - { - "name": "codegen-coverage", - "path": "", - "description": "Coverage fixtures for codegen edge cases", - "input_type": { - "name": "myapi::coverage::CoverageRequest" - }, - "output_kind": "complete", - "output_type": { - "name": "myapi::coverage::CoverageResponse" - }, - "serialization": [ - "json", - "msgpack" - ] - } - ], - "input_types": { - "types": [ - { - "kind": "primitive", - "name": "bool", - "description": "Boolean value" - }, - { - "kind": "primitive", - "name": "chrono::DateTime", - "description": "DateTime at a given timezone (RFC3339 format)", - "parameters": [ - { - "name": "Tz" - } - ], - "fallback": { - "name": "std::string::String" - } - }, - { - "kind": "primitive", - "name": "f64", - "description": "64-bit floating point number" - }, - { - "kind": "primitive", - "name": "i32", - "description": "32-bit signed integer" - }, - { - "kind": "primitive", - "name": "i64", - "description": "64-bit signed integer" - }, - { - "kind": "struct", - "name": "myapi::OrderCoverageRequest", - "fields": { - "named": [ - { - "name": "order", - "type": { - "name": "myapi::order::OrderInsertData" - }, - "required": true - }, - { - "name": "rate_limit", - "type": { - "name": "myapi::order::RateLimit" - }, - "required": true - }, - { - "name": "policy", - "type": { - "name": "myapi::order::Policy", - "arguments": [ - { - "name": "std::string::String" - }, - { - "name": "u32" - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::BaseModel", - "fields": { - "named": [ - { - "name": "label", - "type": { - "name": "std::string::String" - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::CoverageRequest", - "fields": { - "named": [ - { - "name": "keywords", - "type": { - "name": "myapi::coverage::PyKeywordFields" - }, - "required": true - }, - { - "name": "reserved", - "type": { - "name": "myapi::coverage::PydanticReservedFields" - }, - "required": true - }, - { - "name": "tree", - "type": { - "name": "myapi::coverage::TreeNode" - }, - "required": true - }, - { - "name": "mutual", - "type": { - "name": "myapi::coverage::MutualA" - }, - "required": true - }, - { - "name": "generic_tree", - "type": { - "name": "myapi::coverage::GenericTree", - "arguments": [ - { - "name": "i32" - } - ] - }, - "required": true - }, - { - "name": "keyword_variants", - "type": { - "name": "myapi::coverage::KeywordVariants" - }, - "required": true - }, - { - "name": "int_keyed", - "type": { - "name": "myapi::coverage::IntKeyedMap" - }, - "required": true - }, - { - "name": "user_id", - "type": { - "name": "myapi::coverage::UserId" - }, - "required": true - }, - { - "name": "deep_option", - "type": { - "name": "myapi::coverage::DeepOption" - }, - "required": true - }, - { - "name": "shadowing", - "type": { - "name": "myapi::coverage::ShadowingFields" - }, - "required": true - }, - { - "name": "empty", - "type": { - "name": "myapi::coverage::EmptyStruct" - }, - "required": true - }, - { - "name": "weird_doc", - "type": { - "name": "myapi::coverage::WeirdDocstring" - }, - "required": true - }, - { - "name": "shadow_base_model", - "type": { - "name": "myapi::coverage::BaseModel" - }, - "required": true - }, - { - "name": "wrapper_int", - "type": { - "name": "myapi::coverage::Wrapper", - "arguments": [ - { - "name": "i32" - } - ] - }, - "required": true - }, - { - "name": "wrapper_str", - "type": { - "name": "myapi::coverage::Wrapper", - "arguments": [ - { - "name": "std::string::String" - } - ] - }, - "required": true - }, - { - "name": "defaulted", - "type": { - "name": "myapi::coverage::DefaultedField" - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::DeepOption", - "fields": { - "named": [ - { - "name": "maybe_maybe", - "type": { - "name": "reflectapi::Option", - "arguments": [ - { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::string::String" - } - ] - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::DefaultedField", - "fields": { - "named": [ - { - "name": "count", - "type": { - "name": "u32" - } - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::EmptyStruct", - "fields": { - "named": [] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::GenericTree", - "parameters": [ - { - "name": "T" - } - ], - "fields": { - "named": [ - { - "name": "value", - "type": { - "name": "T" - }, - "required": true - }, - { - "name": "children", - "type": { - "name": "std::vec::Vec", - "arguments": [ - { - "name": "myapi::coverage::GenericTree", - "arguments": [ - { - "name": "T" - } - ] - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::IntKeyedMap", - "fields": { - "named": [ - { - "name": "by_id", - "type": { - "name": "std::collections::HashMap", - "arguments": [ - { - "name": "u64" - }, - { - "name": "std::string::String" - } - ] - }, - "required": true - }, - { - "name": "uuid_keyed", - "type": { - "name": "std::collections::HashMap", - "arguments": [ - { - "name": "std::string::String" - }, - { - "name": "std::string::String" - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "enum", - "name": "myapi::coverage::KeywordVariants", - "representation": { - "internal": { - "tag": "kind" - } - }, - "variants": [ - { - "name": "class", - "fields": "none" - }, - { - "name": "lambda", - "fields": { - "named": [ - { - "name": "name", - "type": { - "name": "std::string::String" - }, - "required": true - } - ] - } - }, - { - "name": "return", - "fields": { - "named": [ - { - "name": "value", - "type": { - "name": "i32" - }, - "required": true - } - ] - } - } - ] - }, - { - "kind": "struct", - "name": "myapi::coverage::MutualA", - "fields": { - "named": [ - { - "name": "name", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "b", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::boxed::Box", - "arguments": [ - { - "name": "myapi::coverage::MutualB" - } - ] - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::MutualB", - "fields": { - "named": [ - { - "name": "name", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "a", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::boxed::Box", - "arguments": [ - { - "name": "myapi::coverage::MutualA" - } - ] - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::PyKeywordFields", - "fields": { - "named": [ - { - "name": "class", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "from", - "type": { - "name": "u32" - }, - "required": true - }, - { - "name": "import", - "type": { - "name": "bool" - }, - "required": true - }, - { - "name": "lambda", - "type": { - "name": "i64" - }, - "required": true - }, - { - "name": "return", - "type": { - "name": "std::vec::Vec", - "arguments": [ - { - "name": "u8" - } - ] - }, - "required": true - }, - { - "name": "yield", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::string::String" - } - ] - }, - "required": true - }, - { - "name": "None", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "i32" - } - ] - }, - "required": true - }, - { - "name": "True", - "type": { - "name": "bool" - }, - "required": true - }, - { - "name": "type", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "match", - "type": { - "name": "std::string::String" - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::PydanticReservedFields", - "fields": { - "named": [ - { - "name": "model_config", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "model_fields_set", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "model_dump_json", - "type": { - "name": "std::string::String" - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::ShadowingFields", - "fields": { - "named": [ - { - "name": "field", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "annotated", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "generic", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "base_model", - "type": { - "name": "std::string::String" - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::TreeNode", - "fields": { - "named": [ - { - "name": "value", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "children", - "type": { - "name": "std::vec::Vec", - "arguments": [ - { - "name": "myapi::coverage::TreeNode" - } - ] - }, - "required": true - }, - { - "name": "parent", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::boxed::Box", - "arguments": [ - { - "name": "myapi::coverage::TreeNode" - } - ] - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::UserId", - "fields": { - "unnamed": [ - { - "name": "0", - "type": { - "name": "u64" - }, - "required": true - } - ] - }, - "transparent": true - }, - { - "kind": "struct", - "name": "myapi::coverage::WeirdDocstring", - "description": "A docstring with \"quotes\" and 'apostrophes' and a backslash: \\\\\nAnd a \"\"\"triple quote\"\"\" inside.", - "fields": { - "named": [ - { - "name": "value", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "mixed_quotes", - "description": "Field description with \"double quotes\" and 'single quotes'.", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "doubles_only", - "description": "Field description with only \"double quotes\" — should use single-quoted Python literal.", - "type": { - "name": "std::string::String" - }, - "required": true - } - ] - } - }, + } + ], + "input_types": { + "types": [ { - "kind": "struct", - "name": "myapi::coverage::Wrapper", + "kind": "primitive", + "name": "chrono::DateTime", + "description": "DateTime at a given timezone (RFC3339 format)", "parameters": [ { - "name": "T" + "name": "Tz" } ], - "fields": { - "named": [ - { - "name": "inner", - "type": { - "name": "T" - }, - "required": true - } - ] + "fallback": { + "name": "std::string::String" } }, + { + "kind": "primitive", + "name": "f64", + "description": "64-bit floating point number" + }, { "kind": "enum", "name": "myapi::model::Behavior", @@ -1084,119 +352,6 @@ ] } }, - { - "kind": "struct", - "name": "myapi::order::OrderInsertData", - "description": "A struct whose name begins with the parent namespace cap.\nExercises the namespace-alias path for both the stripped\n(`order.InsertData`) and Rust-leaf (`order.OrderInsertData`)\nforms.", - "fields": { - "named": [ - { - "name": "identity", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "alternative_part_number", - "description": "Exercises the `tuple[A, B]` rendering for `(A, B)`.", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::tuple::Tuple2", - "arguments": [ - { - "name": "std::string::String" - }, - { - "name": "std::string::String" - } - ] - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::order::Policy", - "description": "Exercises `PhantomData` elision (the field carries no wire\ndata and must not appear in the Python model).", - "parameters": [ - { - "name": "C" - }, - { - "name": "T" - } - ], - "fields": { - "named": [ - { - "name": "name", - "type": { - "name": "std::string::String" - }, - "required": true - }, - { - "name": "_context_marker", - "type": { - "name": "std::marker::PhantomData", - "arguments": [ - { - "name": "C" - } - ] - }, - "required": true - }, - { - "name": "_output_marker", - "type": { - "name": "std::marker::PhantomData", - "arguments": [ - { - "name": "T" - } - ] - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::order::RateLimit", - "description": "Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire\nadapter.", - "fields": { - "named": [ - { - "name": "retry_after", - "type": { - "name": "std::time::Duration" - }, - "required": true - }, - { - "name": "max_wait", - "type": { - "name": "std::option::Option", - "arguments": [ - { - "name": "std::time::Duration" - } - ] - }, - "required": true - } - ] - } - }, { "kind": "struct", "name": "myapi::proto::Headers", @@ -1374,42 +529,6 @@ } ] }, - { - "kind": "primitive", - "name": "std::boxed::Box", - "description": "std::boxed::Box pointer type", - "parameters": [ - { - "name": "T" - } - ], - "fallback": { - "name": "T" - } - }, - { - "kind": "primitive", - "name": "std::collections::HashMap", - "description": "Key-value map type", - "parameters": [ - { - "name": "K" - }, - { - "name": "V" - } - ] - }, - { - "kind": "primitive", - "name": "std::marker::PhantomData", - "description": "Zero-sized phantom data", - "parameters": [ - { - "name": "T" - } - ] - }, { "kind": "enum", "name": "std::option::Option", @@ -1447,42 +566,6 @@ "name": "std::string::String", "description": "UTF-8 encoded string" }, - { - "kind": "struct", - "name": "std::time::Duration", - "description": "Time duration type", - "fields": { - "named": [ - { - "name": "secs", - "type": { - "name": "u64" - }, - "required": true - }, - { - "name": "nanos", - "type": { - "name": "u32" - }, - "required": true - } - ] - } - }, - { - "kind": "primitive", - "name": "std::tuple::Tuple2", - "description": "Tuple holding 2 elements", - "parameters": [ - { - "name": "T1" - }, - { - "name": "T2" - } - ] - }, { "kind": "primitive", "name": "std::vec::Vec", @@ -1493,16 +576,6 @@ } ] }, - { - "kind": "primitive", - "name": "u32", - "description": "32-bit unsigned integer" - }, - { - "kind": "primitive", - "name": "u64", - "description": "64-bit unsigned integer" - }, { "kind": "primitive", "name": "u8", @@ -1512,11 +585,6 @@ }, "output_types": { "types": [ - { - "kind": "primitive", - "name": "bool", - "description": "Boolean value" - }, { "kind": "primitive", "name": "chrono::DateTime", @@ -1542,36 +610,6 @@ "named": [] } }, - { - "kind": "struct", - "name": "myapi::OrderCoverageResponse", - "fields": { - "named": [ - { - "name": "ok", - "type": { - "name": "bool" - }, - "required": true - } - ] - } - }, - { - "kind": "struct", - "name": "myapi::coverage::CoverageResponse", - "fields": { - "named": [ - { - "name": "ok", - "type": { - "name": "bool" - }, - "required": true - } - ] - } - }, { "kind": "enum", "name": "myapi::model::Behavior", @@ -1952,12 +990,6 @@ "description": "Struct object with no fields", "fields": "none" }, - { - "kind": "struct", - "name": "reflectapi::Infallible", - "description": "Error object which is expected to be never returned", - "fields": "none" - }, { "kind": "enum", "name": "std::option::Option", diff --git a/reflectapi-demo/src/lib.rs b/reflectapi-demo/src/lib.rs index 473df9b1..9b4e54a0 100644 --- a/reflectapi-demo/src/lib.rs +++ b/reflectapi-demo/src/lib.rs @@ -49,14 +49,6 @@ pub fn builder() -> reflectapi::Builder> { .readonly(true) .description("Stream of change data capture events for pets") }) - .route(order_coverage, |b| { - b.name("codegen-order-coverage") - .description("Coverage fixtures for namespace/tuple/Duration/PhantomData rendering") - }) - .route(codegen_coverage, |b| { - b.name("codegen-coverage") - .description("Coverage fixtures for codegen edge cases") - }) .rename_types("reflectapi_demo::", "myapi::") // and some optional linting rules .validate(|schema| { @@ -91,40 +83,6 @@ async fn health_check( Ok(reflectapi::Empty {}) } -#[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] -pub struct OrderCoverageRequest { - pub order: order::OrderInsertData, - pub rate_limit: order::RateLimit, - pub policy: order::Policy, -} - -#[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] -pub struct OrderCoverageResponse { - pub ok: bool, -} - -/// Endpoint that drags the `order::` coverage fixtures into the -/// schema so the smoke test exercises their rendering. Not intended -/// to be called from the demo server. -async fn order_coverage( - _: Arc, - request: OrderCoverageRequest, - _headers: reflectapi::Empty, -) -> Result { - let _ = request; - Ok(OrderCoverageResponse { ok: true }) -} - -/// Endpoint that drags the `coverage::` fixtures into the schema. -async fn codegen_coverage( - _: Arc, - request: coverage::CoverageRequest, - _headers: reflectapi::Empty, -) -> Result { - let _ = request; - Ok(coverage::CoverageResponse { ok: true }) -} - #[derive(Debug)] pub struct AppState { pets: Mutex>, @@ -140,258 +98,6 @@ impl Default for AppState { } } -// Coverage fixtures exercised by the codegen smoke tests. -mod order { - use std::marker::PhantomData; - use std::time::Duration; - - /// A struct whose name begins with the parent namespace cap. - /// Exercises the namespace-alias path for both the stripped - /// (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) - /// forms. - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct OrderInsertData { - pub identity: String, - /// Exercises the `tuple[A, B]` rendering for `(A, B)`. - pub alternative_part_number: Option<(String, String)>, - } - - /// Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire - /// adapter. - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct RateLimit { - pub retry_after: Duration, - pub max_wait: Option, - } - - /// Exercises `PhantomData` elision (the field carries no wire - /// data and must not appear in the Python model). - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct Policy - where - C: 'static, - T: 'static, - { - pub name: String, - pub _context_marker: PhantomData, - pub _output_marker: PhantomData, - } -} - -/// Coverage fixtures for codegen edge cases. Each type exercises a -/// specific rendering rule; the CI smoke test (regenerate + -/// `import api_client`) confirms every shape round-trips through -/// JSON cleanly. -mod coverage { - use std::collections::HashMap; - - // ---- Python-keyword and builtin field names ---- - // serde serialises these names verbatim; the codegen must produce - // a safe Python identifier and carry the wire name as a Field - // alias so the round-trip is preserved. - - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct PyKeywordFields { - #[serde(rename = "class")] - pub class_: String, - #[serde(rename = "from")] - pub from_: u32, - #[serde(rename = "import")] - pub import_: bool, - #[serde(rename = "lambda")] - pub lambda_: i64, - #[serde(rename = "return")] - pub return_: Vec, - #[serde(rename = "yield")] - pub yield_: Option, - #[serde(rename = "None")] - pub none_: Option, - #[serde(rename = "True")] - pub true_: bool, - // Python builtins that aren't keywords but shadow ergonomics. - pub r#type: String, - pub r#match: String, - } - - // ---- Pydantic-reserved field names ---- - // These names collide with `BaseModel`'s own attributes/methods on - // the Python side; the generated class must rename and alias them. - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct PydanticReservedFields { - #[serde(rename = "model_config")] - pub model_config_: String, - #[serde(rename = "model_fields_set")] - pub model_fields_set_: String, - #[serde(rename = "model_dump_json")] - pub model_dump_json_: String, - } - - // ---- Self-referential type ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct TreeNode { - pub value: String, - pub children: Vec, - pub parent: Option>, - } - - // ---- Mutually recursive types ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct MutualA { - pub name: String, - pub b: Option>, - } - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct MutualB { - pub name: String, - pub a: Option>, - } - - // ---- Generic recursive type ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct GenericTree { - pub value: T, - pub children: Vec>, - } - - // ---- Enum with variants whose names are Python keywords ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - #[serde(tag = "kind", rename_all = "snake_case")] - pub enum KeywordVariants { - Class, - Lambda { name: String }, - Return { value: i32 }, - } - - // ---- HashMap with non-string keys (JSON stringifies them) ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct IntKeyedMap { - pub by_id: HashMap, - pub uuid_keyed: HashMap, - } - - // ---- Transparent newtype (no Python class emitted) ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - #[serde(transparent)] - pub struct UserId(pub u64); - - // ---- Three-state Option wrapping a regular Option ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct DeepOption { - pub maybe_maybe: reflectapi::Option>, - } - - // ---- Field names shared with symbols imported into the - // generated module (`BaseModel`, `Field`, `Annotated`, etc.) ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct ShadowingFields { - pub field: String, - pub annotated: String, - pub generic: String, - pub base_model: String, - } - - // ---- Empty struct ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct EmptyStruct {} - - // ---- Docstring with characters that need escaping ---- - /// A docstring with "quotes" and 'apostrophes' and a backslash: \\ - /// And a """triple quote""" inside. - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct WeirdDocstring { - pub value: String, - /// Field description with "double quotes" and 'single quotes'. - pub mixed_quotes: String, - /// Field description with only "double quotes" — should use single-quoted Python literal. - pub doubles_only: String, - } - - // ---- Type name that shadows a Pydantic-imported symbol ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct BaseModel { - pub label: String, - } - - // ---- Generic used with multiple concrete arguments ---- - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct Wrapper { - pub inner: T, - } - - // ---- Field with a non-None serde default ---- - fn default_count() -> u32 { - 42 - } - #[derive( - Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, - )] - pub struct DefaultedField { - #[serde(default = "default_count")] - pub count: u32, - } - - #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] - pub struct CoverageRequest { - pub keywords: PyKeywordFields, - pub reserved: PydanticReservedFields, - pub tree: TreeNode, - pub mutual: MutualA, - pub generic_tree: GenericTree, - pub keyword_variants: KeywordVariants, - pub int_keyed: IntKeyedMap, - pub user_id: UserId, - pub deep_option: DeepOption, - pub shadowing: ShadowingFields, - pub empty: EmptyStruct, - pub weird_doc: WeirdDocstring, - pub shadow_base_model: BaseModel, - pub wrapper_int: Wrapper, - pub wrapper_str: Wrapper, - pub defaulted: DefaultedField, - } - - #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] - pub struct CoverageResponse { - pub ok: bool, - } -} - mod model { #[derive( Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, diff --git a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap index 59db1954..818dce42 100644 --- a/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap +++ b/reflectapi-demo/src/tests/snapshots/reflectapi_demo__tests__write_openapi_spec.snap @@ -10,64 +10,6 @@ expression: s "version": "1.0.0" }, "paths": { - "/codegen-coverage": { - "description": "Coverage fixtures for codegen edge cases", - "post": { - "operationId": "codegen-coverage", - "description": "Coverage fixtures for codegen edge cases", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.coverage.CoverageRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.coverage.CoverageResponse" - } - } - } - } - } - } - }, - "/codegen-order-coverage": { - "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", - "post": { - "operationId": "codegen-order-coverage", - "description": "Coverage fixtures for namespace/tuple/Duration/PhantomData rendering", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.OrderCoverageRequest" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/myapi.OrderCoverageResponse" - } - } - } - } - } - } - }, "/pets.cdc-events": { "description": "Stream of change data capture events for pets", "post": { @@ -443,534 +385,10 @@ expression: s }, "components": { "schemas": { - "bool": { - "description": "Boolean value", - "type": "boolean" - }, "f64": { "description": "64-bit floating point number", "type": "number" }, - "i32": { - "description": "32-bit signed integer", - "type": "integer" - }, - "i64": { - "description": "64-bit signed integer", - "type": "integer" - }, - "myapi.OrderCoverageRequest": { - "type": "object", - "title": "myapi.OrderCoverageRequest", - "required": [ - "order", - "policy", - "rate_limit" - ], - "properties": { - "order": { - "$ref": "#/components/schemas/myapi.order.OrderInsertData" - }, - "policy": { - "description": "Exercises `PhantomData` elision (the field carries no wire\ndata and must not appear in the Python model).", - "type": "object", - "title": "myapi.order.Policy", - "required": [ - "_context_marker", - "_output_marker", - "name" - ], - "properties": { - "_context_marker": { - "description": "Zero-sized phantom data", - "type": "null" - }, - "_output_marker": { - "description": "Zero-sized phantom data", - "type": "null" - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "rate_limit": { - "$ref": "#/components/schemas/myapi.order.RateLimit" - } - } - }, - "myapi.OrderCoverageResponse": { - "type": "object", - "title": "myapi.OrderCoverageResponse", - "required": [ - "ok" - ], - "properties": { - "ok": { - "$ref": "#/components/schemas/bool" - } - } - }, - "myapi.coverage.BaseModel": { - "type": "object", - "title": "myapi.coverage.BaseModel", - "required": [ - "label" - ], - "properties": { - "label": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.CoverageRequest": { - "type": "object", - "title": "myapi.coverage.CoverageRequest", - "required": [ - "deep_option", - "defaulted", - "empty", - "generic_tree", - "int_keyed", - "keyword_variants", - "keywords", - "mutual", - "reserved", - "shadow_base_model", - "shadowing", - "tree", - "user_id", - "weird_doc", - "wrapper_int", - "wrapper_str" - ], - "properties": { - "deep_option": { - "$ref": "#/components/schemas/myapi.coverage.DeepOption" - }, - "defaulted": { - "$ref": "#/components/schemas/myapi.coverage.DefaultedField" - }, - "empty": { - "$ref": "#/components/schemas/myapi.coverage.EmptyStruct" - }, - "generic_tree": { - "type": "object", - "title": "myapi.coverage.GenericTree", - "required": [ - "children", - "value" - ], - "properties": { - "children": { - "description": "Expandable array type", - "type": "array", - "items": { - "$ref": "#/components/schemas/myapi.coverage.GenericTree_i32_" - } - }, - "value": { - "$ref": "#/components/schemas/i32" - } - } - }, - "int_keyed": { - "$ref": "#/components/schemas/myapi.coverage.IntKeyedMap" - }, - "keyword_variants": { - "$ref": "#/components/schemas/myapi.coverage.KeywordVariants" - }, - "keywords": { - "$ref": "#/components/schemas/myapi.coverage.PyKeywordFields" - }, - "mutual": { - "$ref": "#/components/schemas/myapi.coverage.MutualA" - }, - "reserved": { - "$ref": "#/components/schemas/myapi.coverage.PydanticReservedFields" - }, - "shadow_base_model": { - "$ref": "#/components/schemas/myapi.coverage.BaseModel" - }, - "shadowing": { - "$ref": "#/components/schemas/myapi.coverage.ShadowingFields" - }, - "tree": { - "$ref": "#/components/schemas/myapi.coverage.TreeNode" - }, - "user_id": { - "$ref": "#/components/schemas/u64" - }, - "weird_doc": { - "$ref": "#/components/schemas/myapi.coverage.WeirdDocstring" - }, - "wrapper_int": { - "type": "object", - "title": "myapi.coverage.Wrapper", - "required": [ - "inner" - ], - "properties": { - "inner": { - "$ref": "#/components/schemas/i32" - } - } - }, - "wrapper_str": { - "type": "object", - "title": "myapi.coverage.Wrapper", - "required": [ - "inner" - ], - "properties": { - "inner": { - "$ref": "#/components/schemas/std.string.String" - } - } - } - } - }, - "myapi.coverage.CoverageResponse": { - "type": "object", - "title": "myapi.coverage.CoverageResponse", - "required": [ - "ok" - ], - "properties": { - "ok": { - "$ref": "#/components/schemas/bool" - } - } - }, - "myapi.coverage.DeepOption": { - "type": "object", - "title": "myapi.coverage.DeepOption", - "required": [ - "maybe_maybe" - ], - "properties": { - "maybe_maybe": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/std.string.String" - } - ] - } - ] - } - } - }, - "myapi.coverage.DefaultedField": { - "type": "object", - "title": "myapi.coverage.DefaultedField", - "properties": { - "count": { - "$ref": "#/components/schemas/u32" - } - } - }, - "myapi.coverage.EmptyStruct": { - "type": "object", - "title": "myapi.coverage.EmptyStruct", - "properties": {} - }, - "myapi.coverage.IntKeyedMap": { - "type": "object", - "title": "myapi.coverage.IntKeyedMap", - "required": [ - "by_id", - "uuid_keyed" - ], - "properties": { - "by_id": { - "description": "Key-value map type", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/std.string.String" - } - }, - "uuid_keyed": { - "description": "Key-value map type", - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/std.string.String" - } - } - } - }, - "myapi.coverage.KeywordVariants": { - "oneOf": [ - { - "type": "object", - "title": "class", - "required": [ - "kind" - ], - "properties": { - "kind": { - "const": "class" - } - } - }, - { - "type": "object", - "title": "lambda", - "required": [ - "kind", - "name" - ], - "properties": { - "kind": { - "const": "lambda" - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - { - "type": "object", - "title": "return", - "required": [ - "kind", - "value" - ], - "properties": { - "kind": { - "const": "return" - }, - "value": { - "$ref": "#/components/schemas/i32" - } - } - } - ] - }, - "myapi.coverage.MutualA": { - "type": "object", - "title": "myapi.coverage.MutualA", - "required": [ - "b", - "name" - ], - "properties": { - "b": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/myapi.coverage.MutualB" - } - ] - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.MutualB": { - "type": "object", - "title": "myapi.coverage.MutualB", - "required": [ - "a", - "name" - ], - "properties": { - "a": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/myapi.coverage.MutualA" - } - ] - }, - "name": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.PyKeywordFields": { - "type": "object", - "title": "myapi.coverage.PyKeywordFields", - "required": [ - "None", - "True", - "class", - "from", - "import", - "lambda", - "match", - "return", - "type", - "yield" - ], - "properties": { - "None": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/i32" - } - ] - }, - "True": { - "$ref": "#/components/schemas/bool" - }, - "class": { - "$ref": "#/components/schemas/std.string.String" - }, - "from": { - "$ref": "#/components/schemas/u32" - }, - "import": { - "$ref": "#/components/schemas/bool" - }, - "lambda": { - "$ref": "#/components/schemas/i64" - }, - "match": { - "$ref": "#/components/schemas/std.string.String" - }, - "return": { - "description": "Expandable array type", - "type": "array", - "items": { - "$ref": "#/components/schemas/u8" - } - }, - "type": { - "$ref": "#/components/schemas/std.string.String" - }, - "yield": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/std.string.String" - } - ] - } - } - }, - "myapi.coverage.PydanticReservedFields": { - "type": "object", - "title": "myapi.coverage.PydanticReservedFields", - "required": [ - "model_config", - "model_dump_json", - "model_fields_set" - ], - "properties": { - "model_config": { - "$ref": "#/components/schemas/std.string.String" - }, - "model_dump_json": { - "$ref": "#/components/schemas/std.string.String" - }, - "model_fields_set": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.ShadowingFields": { - "type": "object", - "title": "myapi.coverage.ShadowingFields", - "required": [ - "annotated", - "base_model", - "field", - "generic" - ], - "properties": { - "annotated": { - "$ref": "#/components/schemas/std.string.String" - }, - "base_model": { - "$ref": "#/components/schemas/std.string.String" - }, - "field": { - "$ref": "#/components/schemas/std.string.String" - }, - "generic": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.TreeNode": { - "type": "object", - "title": "myapi.coverage.TreeNode", - "required": [ - "children", - "parent", - "value" - ], - "properties": { - "children": { - "description": "Expandable array type", - "type": "array", - "items": { - "$ref": "#/components/schemas/myapi.coverage.TreeNode" - } - }, - "parent": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/myapi.coverage.TreeNode" - } - ] - }, - "value": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.coverage.WeirdDocstring": { - "description": "A docstring with \"quotes\" and 'apostrophes' and a backslash: \\\\\nAnd a \"\"\"triple quote\"\"\" inside.", - "type": "object", - "title": "myapi.coverage.WeirdDocstring", - "required": [ - "doubles_only", - "mixed_quotes", - "value" - ], - "properties": { - "doubles_only": { - "description": "Field description with only \"double quotes\" — should use single-quoted Python literal.", - "$ref": "#/components/schemas/std.string.String" - }, - "mixed_quotes": { - "description": "Field description with \"double quotes\" and 'single quotes'.", - "$ref": "#/components/schemas/std.string.String" - }, - "value": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, "myapi.model.Behavior": { "oneOf": [ { @@ -1163,66 +581,6 @@ expression: s } } }, - "myapi.order.OrderInsertData": { - "description": "A struct whose name begins with the parent namespace cap.\nExercises the namespace-alias path for both the stripped\n(`order.InsertData`) and Rust-leaf (`order.OrderInsertData`)\nforms.", - "type": "object", - "title": "myapi.order.OrderInsertData", - "required": [ - "alternative_part_number", - "identity" - ], - "properties": { - "alternative_part_number": { - "description": "Exercises the `tuple[A, B]` rendering for `(A, B)`.", - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "description": "Tuple holding 2 elements", - "type": "array", - "prefixItems": [ - { - "$ref": "#/components/schemas/std.string.String" - }, - { - "$ref": "#/components/schemas/std.string.String" - } - ] - } - ] - }, - "identity": { - "$ref": "#/components/schemas/std.string.String" - } - } - }, - "myapi.order.RateLimit": { - "description": "Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire\nadapter.", - "type": "object", - "title": "myapi.order.RateLimit", - "required": [ - "max_wait", - "retry_after" - ], - "properties": { - "max_wait": { - "oneOf": [ - { - "description": "Null", - "type": "null" - }, - { - "$ref": "#/components/schemas/std.time.Duration" - } - ] - }, - "retry_after": { - "$ref": "#/components/schemas/std.time.Duration" - } - } - }, "myapi.proto.InternalError": { "type": "object", "title": "myapi.proto.InternalError", @@ -1488,35 +846,10 @@ expression: s "description": "UTF-8 encoded string", "type": "string" }, - "std.time.Duration": { - "description": "Time duration type", - "type": "object", - "title": "std.time.Duration", - "required": [ - "nanos", - "secs" - ], - "properties": { - "nanos": { - "$ref": "#/components/schemas/u32" - }, - "secs": { - "$ref": "#/components/schemas/u64" - } - } - }, "std.tuple.Tuple0": { "description": "Unit type", "type": "null" }, - "u32": { - "description": "32-bit unsigned integer", - "type": "integer" - }, - "u64": { - "description": "64-bit unsigned integer", - "type": "integer" - }, "u8": { "description": "8-bit unsigned integer", "type": "integer" diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py new file mode 100644 index 00000000..f5cb4ee9 --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py @@ -0,0 +1,17 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from ._client import AsyncClient, Client + +from . import codegen_coverage + +from ._rebuild import rebuild_models as _rebuild_models + +_rebuild_models() + +__all__ = ["AsyncClient", "Client", "codegen_coverage"] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py new file mode 100644 index 00000000..5b060fa8 --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py @@ -0,0 +1,167 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from __future__ import annotations + + +# Standard library imports +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import BaseModel, ConfigDict, Field, RootModel + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +from . import codegen_coverage + +from ._rebuild import rebuild_models as _rebuild_models + +_rebuild_models() + + +class AsyncCoverageClient: + """Async client for coverage operations.""" + + def __init__(self, client: AsyncClientBase) -> None: + self._client = client + + async def edges( + self, + data: Optional[codegen_coverage.coverage.CoverageRequest] = None, + ) -> ApiResponse[codegen_coverage.coverage.CoverageResponse]: + """Coverage fixtures for codegen edge cases + + Args: + data: Request data for the edges operation. + + Returns: + ApiResponse[codegen_coverage.coverage.CoverageResponse]: Response containing codegen_coverage.coverage.CoverageResponse data + """ + path = "/coverage.edges" + + params: dict[str, Any] = {} + return await self._client._make_request( + path, + params=params if params else None, + json_model=data, + response_model=codegen_coverage.coverage.CoverageResponse, + ) + + async def order( + self, + data: Optional[codegen_coverage.OrderCoverageRequest] = None, + ) -> ApiResponse[codegen_coverage.OrderCoverageResponse]: + """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering + + Args: + data: Request data for the order operation. + + Returns: + ApiResponse[codegen_coverage.OrderCoverageResponse]: Response containing codegen_coverage.OrderCoverageResponse data + """ + path = "/coverage.order" + + params: dict[str, Any] = {} + return await self._client._make_request( + path, + params=params if params else None, + json_model=data, + response_model=codegen_coverage.OrderCoverageResponse, + ) + + +class AsyncClient(AsyncClientBase): + """Async client for the API.""" + + def __init__( + self, + base_url: str, + **kwargs: Any, + ) -> None: + super().__init__(base_url, **kwargs) + + self.coverage = AsyncCoverageClient(self) + + +class CoverageClient: + """Synchronous client for coverage operations.""" + + def __init__(self, client: ClientBase) -> None: + self._client = client + + def edges( + self, + data: Optional[codegen_coverage.coverage.CoverageRequest] = None, + ) -> ApiResponse[codegen_coverage.coverage.CoverageResponse]: + """Coverage fixtures for codegen edge cases + + Args: + data: Request data for the edges operation. + + Returns: + ApiResponse[codegen_coverage.coverage.CoverageResponse]: Response containing codegen_coverage.coverage.CoverageResponse data + """ + path = "/coverage.edges" + + params: dict[str, Any] = {} + return self._client._make_request( + path, + params=params if params else None, + json_model=data, + response_model=codegen_coverage.coverage.CoverageResponse, + ) + + def order( + self, + data: Optional[codegen_coverage.OrderCoverageRequest] = None, + ) -> ApiResponse[codegen_coverage.OrderCoverageResponse]: + """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering + + Args: + data: Request data for the order operation. + + Returns: + ApiResponse[codegen_coverage.OrderCoverageResponse]: Response containing codegen_coverage.OrderCoverageResponse data + """ + path = "/coverage.order" + + params: dict[str, Any] = {} + return self._client._make_request( + path, + params=params if params else None, + json_model=data, + response_model=codegen_coverage.OrderCoverageResponse, + ) + + +class Client(ClientBase): + """Synchronous client for the API.""" + + def __init__( + self, + base_url: str, + **kwargs: Any, + ) -> None: + super().__init__(base_url, **kwargs) + + self.coverage = CoverageClient(self) diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py new file mode 100644 index 00000000..dab5fdf7 --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py @@ -0,0 +1,94 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from __future__ import annotations + + +from . import codegen_coverage + +from .codegen_coverage import ( + CodegenCoverageOrderCoverageRequest, + CodegenCoverageOrderCoverageResponse, +) + +from . import codegen_coverage + +from .codegen_coverage.coverage import ( + CodegenCoverageCoverageBaseModel, + CodegenCoverageCoverageDeepOption, + CodegenCoverageCoverageDefaultedField, + CodegenCoverageCoverageEmptyStruct, + CodegenCoverageCoverageGenericTree, + CodegenCoverageCoverageIntKeyedMap, + CodegenCoverageCoverageKeywordVariants, + CodegenCoverageCoverageKeywordVariantsClass, + CodegenCoverageCoverageKeywordVariantsLambda, + CodegenCoverageCoverageKeywordVariantsReturn, + CodegenCoverageCoverageMutualA, + CodegenCoverageCoverageMutualB, + CodegenCoverageCoveragePyKeywordFields, + CodegenCoverageCoveragePydanticReservedFields, + CodegenCoverageCoverageRequest, + CodegenCoverageCoverageResponse, + CodegenCoverageCoverageShadowingFields, + CodegenCoverageCoverageTreeNode, + CodegenCoverageCoverageWeirdDocstring, + CodegenCoverageCoverageWrapper, +) + +from . import codegen_coverage + +from .codegen_coverage.order import ( + CodegenCoverageOrderInsertData, + CodegenCoverageOrderPolicy, + CodegenCoverageOrderRateLimit, +) + + +def rebuild_models() -> None: + codegen_coverage.codegen_coverage = codegen_coverage + codegen_coverage.coverage.codegen_coverage = codegen_coverage + codegen_coverage.order.codegen_coverage = codegen_coverage + errors: list[str] = [] + for _model in [ + CodegenCoverageOrderCoverageRequest, + CodegenCoverageOrderCoverageResponse, + CodegenCoverageCoverageBaseModel, + CodegenCoverageCoverageRequest, + CodegenCoverageCoverageResponse, + CodegenCoverageCoverageDeepOption, + CodegenCoverageCoverageDefaultedField, + CodegenCoverageCoverageEmptyStruct, + CodegenCoverageCoverageGenericTree, + CodegenCoverageCoverageIntKeyedMap, + CodegenCoverageCoverageKeywordVariants, + CodegenCoverageCoverageMutualA, + CodegenCoverageCoverageMutualB, + CodegenCoverageCoveragePyKeywordFields, + CodegenCoverageCoveragePydanticReservedFields, + CodegenCoverageCoverageShadowingFields, + CodegenCoverageCoverageTreeNode, + CodegenCoverageCoverageWeirdDocstring, + CodegenCoverageCoverageWrapper, + CodegenCoverageOrderInsertData, + CodegenCoverageOrderPolicy, + CodegenCoverageOrderRateLimit, + ]: + if not hasattr(_model, "model_rebuild"): + continue + try: + _model.model_rebuild() + except Exception as exc: + errors.append(f" - {_model.__name__}: {type(exc).__name__}: {exc}") + if errors: + raise RuntimeError( + "reflectapi: failed to rebuild " + + str(len(errors)) + + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" + + "\n".join(errors) + ) diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py new file mode 100644 index 00000000..3679a2b2 --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py @@ -0,0 +1,81 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from __future__ import annotations + + +# Standard library imports +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import BaseModel, ConfigDict, Field, RootModel + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class CodegenCoverageOrderCoverageRequest(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + order: codegen_coverage.order.OrderInsertData + rate_limit: codegen_coverage.order.RateLimit + policy: codegen_coverage.order.Policy[str, int] + + +class CodegenCoverageOrderCoverageResponse(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + ok: bool + + +# Public aliases for this module +OrderCoverageRequest = CodegenCoverageOrderCoverageRequest +OrderCoverageResponse = CodegenCoverageOrderCoverageResponse + +from . import coverage +from . import order + +try: + from .._rebuild import rebuild_models as _rebuild_models + + _rebuild_models() +except ImportError: + pass + +__all__ = [ + "CodegenCoverageOrderCoverageRequest", + "CodegenCoverageOrderCoverageResponse", + "OrderCoverageRequest", + "OrderCoverageResponse", + "coverage", + "order", +] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py new file mode 100644 index 00000000..b2e0680e --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py @@ -0,0 +1,329 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from __future__ import annotations + + +# Standard library imports +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import BaseModel, ConfigDict, Field, RootModel + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class CodegenCoverageCoverageBaseModel(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + label: str + + +class CodegenCoverageCoverageRequest(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + keywords: codegen_coverage.coverage.PyKeywordFields + reserved: codegen_coverage.coverage.PydanticReservedFields + tree: codegen_coverage.coverage.TreeNode + mutual: codegen_coverage.coverage.MutualA + generic_tree: codegen_coverage.coverage.GenericTree[int] + keyword_variants: codegen_coverage.coverage.KeywordVariants + int_keyed: codegen_coverage.coverage.IntKeyedMap + user_id: int + deep_option: codegen_coverage.coverage.DeepOption + shadowing: codegen_coverage.coverage.ShadowingFields + empty: codegen_coverage.coverage.EmptyStruct + weird_doc: codegen_coverage.coverage.WeirdDocstring + shadow_base_model: codegen_coverage.coverage.BaseModel + wrapper_int: codegen_coverage.coverage.Wrapper[int] + wrapper_str: codegen_coverage.coverage.Wrapper[str] + defaulted: codegen_coverage.coverage.DefaultedField + + +class CodegenCoverageCoverageResponse(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + ok: bool + + +class CodegenCoverageCoverageDeepOption(ReflectapiPartialModel): + model_config = ConfigDict( + extra="ignore", + populate_by_name=True, + validate_assignment=True, + protected_namespaces=(), + ) + + maybe_maybe: str | None | None = None + + +class CodegenCoverageCoverageDefaultedField(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + count: int | None = None + + +class CodegenCoverageCoverageEmptyStruct(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + +class CodegenCoverageCoverageGenericTree(BaseModel, Generic[T]): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: T + children: list[codegen_coverage.coverage.GenericTree[T]] + + +class CodegenCoverageCoverageIntKeyedMap(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + by_id: dict[int, str] + uuid_keyed: dict[str, str] + + +class CodegenCoverageCoverageMutualA(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + b: codegen_coverage.coverage.MutualB | None = None + + +class CodegenCoverageCoverageMutualB(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + a: codegen_coverage.coverage.MutualA | None = None + + +class CodegenCoverageCoveragePyKeywordFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + class_: str = Field(serialization_alias="class", validation_alias="class") + from_: int = Field(serialization_alias="from", validation_alias="from") + import_: bool = Field(serialization_alias="import", validation_alias="import") + lambda_: int = Field(serialization_alias="lambda", validation_alias="lambda") + return_: list[int] = Field(serialization_alias="return", validation_alias="return") + yield_: str | None = Field( + default=None, serialization_alias="yield", validation_alias="yield" + ) + none: int | None = Field( + default=None, serialization_alias="None", validation_alias="None" + ) + true: bool = Field(serialization_alias="True", validation_alias="True") + type_: str = Field(serialization_alias="type", validation_alias="type") + match: str + + +class CodegenCoverageCoveragePydanticReservedFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + model_config_: str = Field( + serialization_alias="model_config", validation_alias="model_config" + ) + model_fields_set_: str = Field( + serialization_alias="model_fields_set", validation_alias="model_fields_set" + ) + model_dump_json_: str = Field( + serialization_alias="model_dump_json", validation_alias="model_dump_json" + ) + + +class CodegenCoverageCoverageShadowingFields(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + field: str + annotated: str + generic: str + base_model: str + + +class CodegenCoverageCoverageTreeNode(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: str + children: list[codegen_coverage.coverage.TreeNode] + parent: codegen_coverage.coverage.TreeNode | None = None + + +class CodegenCoverageCoverageWeirdDocstring(BaseModel): + """A docstring with "quotes" and 'apostrophes' and a backslash: \\\\ +And a \"\"\"triple quote\"\"\" inside.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + value: str + mixed_quotes: str = Field( + description="Field description with \"double quotes\" and 'single quotes'." + ) + doubles_only: str = Field( + description='Field description with only "double quotes" — should use single-quoted Python literal.' + ) + + +class CodegenCoverageCoverageWrapper(BaseModel, Generic[T]): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + inner: T + + +class CodegenCoverageCoverageKeywordVariantsClass(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["class"] = Field(default="class", description="Discriminator field") + + +class CodegenCoverageCoverageKeywordVariantsLambda(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["lambda"] = Field(default="lambda", description="Discriminator field") + name: str + + +class CodegenCoverageCoverageKeywordVariantsReturn(BaseModel): + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + kind: Literal["return"] = Field(default="return", description="Discriminator field") + value: int + + +class CodegenCoverageCoverageKeywordVariants(RootModel): + root: Annotated[ + Union[ + CodegenCoverageCoverageKeywordVariantsClass, + CodegenCoverageCoverageKeywordVariantsLambda, + CodegenCoverageCoverageKeywordVariantsReturn, + ], + Field(discriminator="kind"), + ] + + +# Public aliases for this module +BaseModel = CodegenCoverageCoverageBaseModel +CoverageRequest = CodegenCoverageCoverageRequest +CoverageResponse = CodegenCoverageCoverageResponse +DeepOption = CodegenCoverageCoverageDeepOption +DefaultedField = CodegenCoverageCoverageDefaultedField +EmptyStruct = CodegenCoverageCoverageEmptyStruct +GenericTree = CodegenCoverageCoverageGenericTree +IntKeyedMap = CodegenCoverageCoverageIntKeyedMap +KeywordVariants = CodegenCoverageCoverageKeywordVariants +KeywordVariantsClass = CodegenCoverageCoverageKeywordVariantsClass +KeywordVariantsLambda = CodegenCoverageCoverageKeywordVariantsLambda +KeywordVariantsReturn = CodegenCoverageCoverageKeywordVariantsReturn +MutualA = CodegenCoverageCoverageMutualA +MutualB = CodegenCoverageCoverageMutualB +PyKeywordFields = CodegenCoverageCoveragePyKeywordFields +PydanticReservedFields = CodegenCoverageCoveragePydanticReservedFields +Request = CodegenCoverageCoverageRequest +Response = CodegenCoverageCoverageResponse +ShadowingFields = CodegenCoverageCoverageShadowingFields +TreeNode = CodegenCoverageCoverageTreeNode +WeirdDocstring = CodegenCoverageCoverageWeirdDocstring +Wrapper = CodegenCoverageCoverageWrapper + +__all__ = [ + "BaseModel", + "CodegenCoverageCoverageBaseModel", + "CodegenCoverageCoverageDeepOption", + "CodegenCoverageCoverageDefaultedField", + "CodegenCoverageCoverageEmptyStruct", + "CodegenCoverageCoverageGenericTree", + "CodegenCoverageCoverageIntKeyedMap", + "CodegenCoverageCoverageKeywordVariants", + "CodegenCoverageCoverageKeywordVariantsClass", + "CodegenCoverageCoverageKeywordVariantsLambda", + "CodegenCoverageCoverageKeywordVariantsReturn", + "CodegenCoverageCoverageMutualA", + "CodegenCoverageCoverageMutualB", + "CodegenCoverageCoveragePyKeywordFields", + "CodegenCoverageCoveragePydanticReservedFields", + "CodegenCoverageCoverageRequest", + "CodegenCoverageCoverageResponse", + "CodegenCoverageCoverageShadowingFields", + "CodegenCoverageCoverageTreeNode", + "CodegenCoverageCoverageWeirdDocstring", + "CodegenCoverageCoverageWrapper", + "CoverageRequest", + "CoverageResponse", + "DeepOption", + "DefaultedField", + "EmptyStruct", + "GenericTree", + "IntKeyedMap", + "KeywordVariants", + "KeywordVariantsClass", + "KeywordVariantsLambda", + "KeywordVariantsReturn", + "MutualA", + "MutualB", + "PyKeywordFields", + "PydanticReservedFields", + "Request", + "Response", + "ShadowingFields", + "TreeNode", + "WeirdDocstring", + "Wrapper", +] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py new file mode 100644 index 00000000..2a313c76 --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py @@ -0,0 +1,95 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from __future__ import annotations + + +# Standard library imports +from enum import Enum +from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union + +# Third-party imports +from pydantic import BaseModel, ConfigDict, Field, RootModel + +# Runtime imports +from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse +from reflectapi_runtime import ReflectapiDuration +from reflectapi_runtime import ReflectapiEmpty +from reflectapi_runtime import ReflectapiInfallible +from reflectapi_runtime import ReflectapiPartialModel + + +# Type variables for generic types + + +C = TypeVar("C") + +T = TypeVar("T") + + +# External type definitions +StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] +StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] +StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] +StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] + + +class CodegenCoverageOrderInsertData(BaseModel): + """A struct whose name begins with the parent namespace cap. + Exercises the namespace-alias path for both the stripped + (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) + forms.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + identity: str + alternative_part_number: tuple[str, str] | None = Field( + default=None, description="Exercises the `tuple[A, B]` rendering for `(A, B)`." + ) + + +class CodegenCoverageOrderPolicy(BaseModel, Generic[C, T]): + """Exercises `PhantomData` elision (the field carries no wire + data and must not appear in the Python model).""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + name: str + + +class CodegenCoverageOrderRateLimit(BaseModel): + """Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire + adapter.""" + + model_config = ConfigDict( + extra="ignore", populate_by_name=True, protected_namespaces=() + ) + + retry_after: ReflectapiDuration + max_wait: ReflectapiDuration | None = None + + +# Public aliases for this module +InsertData = CodegenCoverageOrderInsertData +OrderInsertData = CodegenCoverageOrderInsertData +Policy = CodegenCoverageOrderPolicy +RateLimit = CodegenCoverageOrderRateLimit + +__all__ = [ + "CodegenCoverageOrderInsertData", + "CodegenCoverageOrderPolicy", + "CodegenCoverageOrderRateLimit", + "InsertData", + "OrderInsertData", + "Policy", + "RateLimit", +] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py new file mode 100644 index 00000000..5a62f8fa --- /dev/null +++ b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py @@ -0,0 +1,88 @@ +""" +DO NOT MODIFY THIS FILE MANUALLY +This file was generated by reflectapi-cli + +Schema name: Codegen coverage +Internal test schema for Python codegen smoke tests +""" + +from __future__ import annotations + + +from ._client import AsyncClient + +from ._client import Client + +from . import codegen_coverage + +from .codegen_coverage import ( + CodegenCoverageOrderCoverageRequest, + CodegenCoverageOrderCoverageResponse, +) + +from . import codegen_coverage + +from .codegen_coverage.coverage import ( + CodegenCoverageCoverageBaseModel, + CodegenCoverageCoverageDeepOption, + CodegenCoverageCoverageDefaultedField, + CodegenCoverageCoverageEmptyStruct, + CodegenCoverageCoverageGenericTree, + CodegenCoverageCoverageIntKeyedMap, + CodegenCoverageCoverageKeywordVariants, + CodegenCoverageCoverageKeywordVariantsClass, + CodegenCoverageCoverageKeywordVariantsLambda, + CodegenCoverageCoverageKeywordVariantsReturn, + CodegenCoverageCoverageMutualA, + CodegenCoverageCoverageMutualB, + CodegenCoverageCoveragePyKeywordFields, + CodegenCoverageCoveragePydanticReservedFields, + CodegenCoverageCoverageRequest, + CodegenCoverageCoverageResponse, + CodegenCoverageCoverageShadowingFields, + CodegenCoverageCoverageTreeNode, + CodegenCoverageCoverageWeirdDocstring, + CodegenCoverageCoverageWrapper, +) + +from . import codegen_coverage + +from .codegen_coverage.order import ( + CodegenCoverageOrderInsertData, + CodegenCoverageOrderPolicy, + CodegenCoverageOrderRateLimit, +) + +from ._rebuild import rebuild_models as _rebuild_models + +_rebuild_models() + +__all__ = [ + "AsyncClient", + "Client", + "CodegenCoverageCoverageBaseModel", + "CodegenCoverageCoverageDeepOption", + "CodegenCoverageCoverageDefaultedField", + "CodegenCoverageCoverageEmptyStruct", + "CodegenCoverageCoverageGenericTree", + "CodegenCoverageCoverageIntKeyedMap", + "CodegenCoverageCoverageKeywordVariants", + "CodegenCoverageCoverageKeywordVariantsClass", + "CodegenCoverageCoverageKeywordVariantsLambda", + "CodegenCoverageCoverageKeywordVariantsReturn", + "CodegenCoverageCoverageMutualA", + "CodegenCoverageCoverageMutualB", + "CodegenCoverageCoveragePyKeywordFields", + "CodegenCoverageCoveragePydanticReservedFields", + "CodegenCoverageCoverageRequest", + "CodegenCoverageCoverageResponse", + "CodegenCoverageCoverageShadowingFields", + "CodegenCoverageCoverageTreeNode", + "CodegenCoverageCoverageWeirdDocstring", + "CodegenCoverageCoverageWrapper", + "CodegenCoverageOrderCoverageRequest", + "CodegenCoverageOrderCoverageResponse", + "CodegenCoverageOrderInsertData", + "CodegenCoverageOrderPolicy", + "CodegenCoverageOrderRateLimit", +] diff --git a/reflectapi-demo/tests/codegen_coverage.rs b/reflectapi-demo/tests/codegen_coverage.rs new file mode 100644 index 00000000..fd875a44 --- /dev/null +++ b/reflectapi-demo/tests/codegen_coverage.rs @@ -0,0 +1,331 @@ +//! Codegen coverage fixtures + smoke test. +//! +//! Every type in this file exercises a specific Python codegen +//! rendering path. They live here as an integration test (rather +//! than in the demo's public schema) so the demo stays a clean +//! example and the fixtures stay clearly test-only. +//! +//! The `#[test]` at the bottom builds a private schema from these +//! fixtures, runs the Python codegen against it, and writes the +//! result to `target/codegen-coverage-client/`. CI imports that +//! generated package — the strict `_rebuild_models()` raises on any +//! dangling type reference. + +use std::sync::Arc; + +#[derive(Debug, Default)] +struct State; + +#[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] +pub struct OrderCoverageRequest { + pub order: order::OrderInsertData, + pub rate_limit: order::RateLimit, + pub policy: order::Policy, +} + +#[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] +pub struct OrderCoverageResponse { + pub ok: bool, +} + +async fn order_coverage( + _: Arc, + request: OrderCoverageRequest, + _headers: reflectapi::Empty, +) -> Result { + let _ = request; + Ok(OrderCoverageResponse { ok: true }) +} + +async fn codegen_coverage( + _: Arc, + request: coverage::CoverageRequest, + _headers: reflectapi::Empty, +) -> Result { + let _ = request; + Ok(coverage::CoverageResponse { ok: true }) +} + +fn builder() -> reflectapi::Builder> { + reflectapi::Builder::new() + .name("Codegen coverage") + .description("Internal test schema for Python codegen smoke tests") + .route(order_coverage, |b| { + b.name("coverage.order") + .description("Coverage fixtures for namespace/tuple/Duration/PhantomData rendering") + }) + .route(codegen_coverage, |b| { + b.name("coverage.edges") + .description("Coverage fixtures for codegen edge cases") + }) +} + +#[test] +fn write_python_client() { + let (schema, _) = builder().build().unwrap(); + let files = reflectapi::codegen::python::generate_files( + schema, + &reflectapi::codegen::python::Config { + package_name: "codegen_coverage_client".into(), + generate_async: true, + generate_sync: true, + generate_testing: false, + format: true, + base_url: None, + }, + ) + .unwrap(); + + let out_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("target") + .join("codegen-coverage-client") + .join("codegen_coverage_client"); + // Wipe between runs so removed files don't linger as stale orphans. + let _ = std::fs::remove_dir_all(&out_dir); + for (filename, src) in files { + let path = out_dir.join(filename); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent).unwrap(); + } + std::fs::write(path, src).unwrap(); + } +} + +mod order { + use std::marker::PhantomData; + use std::time::Duration; + + /// A struct whose name begins with the parent namespace cap. + /// Exercises the namespace-alias path for both the stripped + /// (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) + /// forms. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct OrderInsertData { + pub identity: String, + /// Exercises the `tuple[A, B]` rendering for `(A, B)`. + pub alternative_part_number: Option<(String, String)>, + } + + /// Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire + /// adapter. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct RateLimit { + pub retry_after: Duration, + pub max_wait: Option, + } + + /// Exercises `PhantomData` elision (the field carries no wire + /// data and must not appear in the Python model). + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct Policy + where + C: 'static, + T: 'static, + { + pub name: String, + pub _context_marker: PhantomData, + pub _output_marker: PhantomData, + } +} + +mod coverage { + use std::collections::HashMap; + + // ---- Python-keyword and builtin field names ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct PyKeywordFields { + #[serde(rename = "class")] + pub class_: String, + #[serde(rename = "from")] + pub from_: u32, + #[serde(rename = "import")] + pub import_: bool, + #[serde(rename = "lambda")] + pub lambda_: i64, + #[serde(rename = "return")] + pub return_: Vec, + #[serde(rename = "yield")] + pub yield_: Option, + #[serde(rename = "None")] + pub none_: Option, + #[serde(rename = "True")] + pub true_: bool, + pub r#type: String, + pub r#match: String, + } + + // ---- Pydantic-reserved field names ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct PydanticReservedFields { + #[serde(rename = "model_config")] + pub model_config_: String, + #[serde(rename = "model_fields_set")] + pub model_fields_set_: String, + #[serde(rename = "model_dump_json")] + pub model_dump_json_: String, + } + + // ---- Self-referential type ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct TreeNode { + pub value: String, + pub children: Vec, + pub parent: Option>, + } + + // ---- Mutually recursive types ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct MutualA { + pub name: String, + pub b: Option>, + } + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct MutualB { + pub name: String, + pub a: Option>, + } + + // ---- Generic recursive type ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct GenericTree { + pub value: T, + pub children: Vec>, + } + + // ---- Enum with variants whose names are Python keywords ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + #[serde(tag = "kind", rename_all = "snake_case")] + pub enum KeywordVariants { + Class, + Lambda { name: String }, + Return { value: i32 }, + } + + // ---- HashMap with non-string keys ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct IntKeyedMap { + pub by_id: HashMap, + pub uuid_keyed: HashMap, + } + + // ---- Transparent newtype ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + #[serde(transparent)] + pub struct UserId(pub u64); + + // ---- Three-state Option wrapping a regular Option ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct DeepOption { + pub maybe_maybe: reflectapi::Option>, + } + + // ---- Field names shared with module imports ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct ShadowingFields { + pub field: String, + pub annotated: String, + pub generic: String, + pub base_model: String, + } + + // ---- Empty struct ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct EmptyStruct {} + + // ---- Docstring with characters that need escaping ---- + /// A docstring with "quotes" and 'apostrophes' and a backslash: \\ + /// And a """triple quote""" inside. + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct WeirdDocstring { + pub value: String, + /// Field description with "double quotes" and 'single quotes'. + pub mixed_quotes: String, + /// Field description with only "double quotes" — should use single-quoted Python literal. + pub doubles_only: String, + } + + // ---- Type name that shadows a Pydantic-imported symbol ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct BaseModel { + pub label: String, + } + + // ---- Generic used with multiple concrete arguments ---- + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct Wrapper { + pub inner: T, + } + + // ---- Field with a non-None serde default ---- + fn default_count() -> u32 { + 42 + } + #[derive( + Debug, Clone, serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output, + )] + pub struct DefaultedField { + #[serde(default = "default_count")] + pub count: u32, + } + + #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] + pub struct CoverageRequest { + pub keywords: PyKeywordFields, + pub reserved: PydanticReservedFields, + pub tree: TreeNode, + pub mutual: MutualA, + pub generic_tree: GenericTree, + pub keyword_variants: KeywordVariants, + pub int_keyed: IntKeyedMap, + pub user_id: UserId, + pub deep_option: DeepOption, + pub shadowing: ShadowingFields, + pub empty: EmptyStruct, + pub weird_doc: WeirdDocstring, + pub shadow_base_model: BaseModel, + pub wrapper_int: Wrapper, + pub wrapper_str: Wrapper, + pub defaulted: DefaultedField, + } + + #[derive(serde::Serialize, serde::Deserialize, reflectapi::Input, reflectapi::Output)] + pub struct CoverageResponse { + pub ok: bool, + } +} diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/client.py b/reflectapi-python-runtime/src/reflectapi_runtime/client.py index 323975bd..d3cf009b 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/client.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/client.py @@ -253,12 +253,16 @@ def _serialize_request_body( ) -> tuple[bytes, dict[str, str]]: """Serialize the request body. - Generated models that contain partial (``reflectapi::Option``) - fields inherit from :class:`ReflectapiPartialModel`, whose - ``@model_serializer`` already omits keys not in - ``model_fields_set``. Plain Pydantic models serialise normally. - Either way, ``model_dump_json(by_alias=True)`` is the right call — - the model decides what goes on the wire, not the transport. + ``ReflectapiPartialModel`` subclasses carry their own + ``@model_serializer`` that omits keys not in + ``model_fields_set``, so explicit ``None`` values must reach + the wire (they encode the protocol's "explicit null" state). + + Plain Pydantic models use ``exclude_none=True`` so an unset + optional field renders as an absent key rather than a JSON + ``null`` — matching what serde sees as + ``#[serde(skip_serializing_if = "Option::is_none")]`` and + preserving the historical wire format. """ # Handle primitive types (for untagged unions) if not hasattr(json_model, "model_dump_json"): @@ -267,7 +271,14 @@ def _serialize_request_body( ).encode("utf-8") return content, {"Content-Type": "application/json"} - content = json_model.model_dump_json(by_alias=True).encode("utf-8") + from .partial import ReflectapiPartialModel + + if isinstance(json_model, ReflectapiPartialModel): + content = json_model.model_dump_json(by_alias=True).encode("utf-8") + else: + content = json_model.model_dump_json( + by_alias=True, exclude_none=True + ).encode("utf-8") return content, {"Content-Type": "application/json"} def _build_headers( @@ -805,12 +816,16 @@ def _serialize_request_body( ) -> tuple[bytes, dict[str, str]]: """Serialize the request body. - Generated models that contain partial (``reflectapi::Option``) - fields inherit from :class:`ReflectapiPartialModel`, whose - ``@model_serializer`` already omits keys not in - ``model_fields_set``. Plain Pydantic models serialise normally. - Either way, ``model_dump_json(by_alias=True)`` is the right call — - the model decides what goes on the wire, not the transport. + ``ReflectapiPartialModel`` subclasses carry their own + ``@model_serializer`` that omits keys not in + ``model_fields_set``, so explicit ``None`` values must reach + the wire (they encode the protocol's "explicit null" state). + + Plain Pydantic models use ``exclude_none=True`` so an unset + optional field renders as an absent key rather than a JSON + ``null`` — matching what serde sees as + ``#[serde(skip_serializing_if = "Option::is_none")]`` and + preserving the historical wire format. """ # Handle primitive types (for untagged unions) if not hasattr(json_model, "model_dump_json"): @@ -819,7 +834,14 @@ def _serialize_request_body( ).encode("utf-8") return content, {"Content-Type": "application/json"} - content = json_model.model_dump_json(by_alias=True).encode("utf-8") + from .partial import ReflectapiPartialModel + + if isinstance(json_model, ReflectapiPartialModel): + content = json_model.model_dump_json(by_alias=True).encode("utf-8") + else: + content = json_model.model_dump_json( + by_alias=True, exclude_none=True + ).encode("utf-8") return content, {"Content-Type": "application/json"} def _build_headers( diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py b/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py index c226e0f1..8c7bc30a 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/streaming.py @@ -236,12 +236,21 @@ async def stream_request( request_headers = headers.copy() if headers else {} - # Serialize Pydantic model. ReflectapiPartialModel emits its own - # @model_serializer that omits keys not in `model_fields_set`, so - # plain `model_dump_json(by_alias=True)` produces the right wire - # shape for both partial and plain generated models. + # Serialize Pydantic model. `ReflectapiPartialModel` emits its + # own @model_serializer that omits keys not in + # `model_fields_set`; explicit `None` values must round-trip + # for it. Plain Pydantic models use `exclude_none=True` so + # unset optional fields stay absent on the wire (matches + # serde's `skip_serializing_if = "Option::is_none"`). if json_model is not None: - content = json_model.model_dump_json(by_alias=True).encode("utf-8") + from .partial import ReflectapiPartialModel + + if isinstance(json_model, ReflectapiPartialModel): + content = json_model.model_dump_json(by_alias=True).encode("utf-8") + else: + content = json_model.model_dump_json( + by_alias=True, exclude_none=True + ).encode("utf-8") request_headers["Content-Type"] = "application/json" request = self._client.build_request( diff --git a/reflectapi-python-runtime/tests/test_codegen_regressions.py b/reflectapi-python-runtime/tests/test_codegen_regressions.py index b31e28a6..57d50b59 100644 --- a/reflectapi-python-runtime/tests/test_codegen_regressions.py +++ b/reflectapi-python-runtime/tests/test_codegen_regressions.py @@ -80,3 +80,72 @@ def test_invalid_dict_shape_falls_through_to_pydantic_error(self): M = self._model() with pytest.raises(ValidationError): M.model_validate({"d": {"secs": "not-a-number"}}) + + +class TestRequestSerializationOmitsUnsetOptional: + """Plain ``BaseModel`` request types must omit unset optional fields + from the wire, matching what ``#[serde(skip_serializing_if = + "Option::is_none")]`` produces on the server side. Partial models, + by contrast, must keep explicit ``None`` to preserve the + absent-vs-null distinction. + """ + + def _client(self): + from reflectapi_runtime import ClientBase + + return ClientBase("http://test") + + def test_plain_model_omits_unset_optional(self): + from reflectapi_runtime import ReflectapiPartialModel # noqa: F401 + + class Req(BaseModel): + cursor: str | None = None + limit: int | None = None + + client = self._client() + # `Req()` leaves both fields at their `None` default; the wire + # payload should drop them. + body, _headers = client._serialize_request_body(Req()) + assert body == b"{}" + + def test_plain_model_keeps_explicit_value(self): + class Req(BaseModel): + cursor: str | None = None + limit: int | None = None + + client = self._client() + body, _ = client._serialize_request_body(Req(cursor="abc", limit=10)) + assert body == b'{"cursor":"abc","limit":10}' + + def test_partial_model_emits_explicit_null(self): + """``ReflectapiPartialModel`` must round-trip explicit ``None`` + as ``null`` (the wire's "field present, value cleared" state).""" + from reflectapi_runtime import ReflectapiPartialModel + + class Req(ReflectapiPartialModel): + model_config = { + "extra": "ignore", + "populate_by_name": True, + "validate_assignment": True, + } + cursor: str | None = None + limit: int | None = None + + client = self._client() + body, _ = client._serialize_request_body(Req(cursor=None, limit=10)) + # Both keys were set (one to None, one to a value) — both go on the wire. + assert b'"cursor":null' in body + assert b'"limit":10' in body + + def test_partial_model_omits_unset_field(self): + from reflectapi_runtime import ReflectapiPartialModel + + class Req(ReflectapiPartialModel): + cursor: str | None = None + limit: int | None = None + + client = self._client() + body, _ = client._serialize_request_body(Req(limit=10)) + # `cursor` was not set on the instance — not in `model_fields_set` → omitted. + assert b"cursor" not in body + assert b'"limit":10' in body From 993788be6d4e510ba9d227c1575eb636aa641eb1 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 16:02:21 +1200 Subject: [PATCH 16/17] chore: untrack coverage-client build artefacts + tidy comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Gitignore reflectapi-demo/target/ so codegen coverage-client outputs stay out of the index. - Migrate the demo client pyproject from the deprecated `[tool.uv] dev-dependencies = …` to the standard `[dependency-groups] dev = …` shape; clears the uv warning. - Tighten coverage test + CI comments to describe the current behaviour rather than contrast with prior state. --- .github/workflows/ci.yaml | 10 +- .gitignore | 1 + reflectapi-demo/clients/python/pyproject.toml | 4 +- .../codegen_coverage_client/__init__.py | 17 - .../codegen_coverage_client/_client.py | 167 --------- .../codegen_coverage_client/_rebuild.py | 94 ----- .../codegen_coverage/__init__.py | 81 ----- .../codegen_coverage/coverage/__init__.py | 329 ------------------ .../codegen_coverage/order/__init__.py | 95 ----- .../codegen_coverage_client/generated.py | 88 ----- reflectapi-demo/tests/codegen_coverage.rs | 22 +- .../src/reflectapi_runtime/client.py | 14 +- 12 files changed, 26 insertions(+), 896 deletions(-) delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py delete mode 100644 reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eb54d874..a6ebbd10 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -74,12 +74,12 @@ jobs: run: uv run python -c "import api_client; print('demo client import OK')" - name: Demo snapshot is committed (no drift) run: git diff --exit-code reflectapi-demo/clients/python/api_client/ - # Codegen coverage fixtures live in the integration test at - # `reflectapi-demo/tests/codegen_coverage.rs` (not in the - # public demo schema). The test writes a Python client to + # Codegen coverage fixtures (recursive types, Python keyword + # field names, Pydantic-reserved names, ...) live in the + # integration test at `reflectapi-demo/tests/codegen_coverage.rs`. + # The test writes a Python client to # `target/codegen-coverage-client/`; the next step imports it - # so the strict `_rebuild_models()` raises on any dangling - # reference. + # so `_rebuild_models()` raises on any dangling reference. - name: Generate codegen coverage client run: cargo test -p reflectapi-demo --test codegen_coverage - name: Import codegen coverage client diff --git a/.gitignore b/.gitignore index f17784cb..98d9207e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /target +reflectapi-demo/target/ docs/book .vscode/settings.json *.pyc diff --git a/reflectapi-demo/clients/python/pyproject.toml b/reflectapi-demo/clients/python/pyproject.toml index 7c979e28..0234a8dc 100644 --- a/reflectapi-demo/clients/python/pyproject.toml +++ b/reflectapi-demo/clients/python/pyproject.toml @@ -15,8 +15,8 @@ packages = ["api_client"] [tool.hatch.metadata] allow-direct-references = true -[tool.uv] -dev-dependencies = ["pytest>=8.0.0", "pytest-asyncio>=0.21.0", "ruff>=0.12.5"] +[dependency-groups] +dev = ["pytest>=8.0.0", "pytest-asyncio>=0.21.0", "ruff>=0.12.5"] [tool.uv.sources] reflectapi-runtime = { path = "../../../reflectapi-python-runtime", editable = true } diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py deleted file mode 100644 index f5cb4ee9..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from ._client import AsyncClient, Client - -from . import codegen_coverage - -from ._rebuild import rebuild_models as _rebuild_models - -_rebuild_models() - -__all__ = ["AsyncClient", "Client", "codegen_coverage"] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py deleted file mode 100644 index 5b060fa8..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_client.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from __future__ import annotations - - -# Standard library imports -from enum import Enum -from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union - -# Third-party imports -from pydantic import BaseModel, ConfigDict, Field, RootModel - -# Runtime imports -from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration -from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible -from reflectapi_runtime import ReflectapiPartialModel - - -# Type variables for generic types - - -C = TypeVar("C") - -T = TypeVar("T") - - -from . import codegen_coverage - -from ._rebuild import rebuild_models as _rebuild_models - -_rebuild_models() - - -class AsyncCoverageClient: - """Async client for coverage operations.""" - - def __init__(self, client: AsyncClientBase) -> None: - self._client = client - - async def edges( - self, - data: Optional[codegen_coverage.coverage.CoverageRequest] = None, - ) -> ApiResponse[codegen_coverage.coverage.CoverageResponse]: - """Coverage fixtures for codegen edge cases - - Args: - data: Request data for the edges operation. - - Returns: - ApiResponse[codegen_coverage.coverage.CoverageResponse]: Response containing codegen_coverage.coverage.CoverageResponse data - """ - path = "/coverage.edges" - - params: dict[str, Any] = {} - return await self._client._make_request( - path, - params=params if params else None, - json_model=data, - response_model=codegen_coverage.coverage.CoverageResponse, - ) - - async def order( - self, - data: Optional[codegen_coverage.OrderCoverageRequest] = None, - ) -> ApiResponse[codegen_coverage.OrderCoverageResponse]: - """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering - - Args: - data: Request data for the order operation. - - Returns: - ApiResponse[codegen_coverage.OrderCoverageResponse]: Response containing codegen_coverage.OrderCoverageResponse data - """ - path = "/coverage.order" - - params: dict[str, Any] = {} - return await self._client._make_request( - path, - params=params if params else None, - json_model=data, - response_model=codegen_coverage.OrderCoverageResponse, - ) - - -class AsyncClient(AsyncClientBase): - """Async client for the API.""" - - def __init__( - self, - base_url: str, - **kwargs: Any, - ) -> None: - super().__init__(base_url, **kwargs) - - self.coverage = AsyncCoverageClient(self) - - -class CoverageClient: - """Synchronous client for coverage operations.""" - - def __init__(self, client: ClientBase) -> None: - self._client = client - - def edges( - self, - data: Optional[codegen_coverage.coverage.CoverageRequest] = None, - ) -> ApiResponse[codegen_coverage.coverage.CoverageResponse]: - """Coverage fixtures for codegen edge cases - - Args: - data: Request data for the edges operation. - - Returns: - ApiResponse[codegen_coverage.coverage.CoverageResponse]: Response containing codegen_coverage.coverage.CoverageResponse data - """ - path = "/coverage.edges" - - params: dict[str, Any] = {} - return self._client._make_request( - path, - params=params if params else None, - json_model=data, - response_model=codegen_coverage.coverage.CoverageResponse, - ) - - def order( - self, - data: Optional[codegen_coverage.OrderCoverageRequest] = None, - ) -> ApiResponse[codegen_coverage.OrderCoverageResponse]: - """Coverage fixtures for namespace/tuple/Duration/PhantomData rendering - - Args: - data: Request data for the order operation. - - Returns: - ApiResponse[codegen_coverage.OrderCoverageResponse]: Response containing codegen_coverage.OrderCoverageResponse data - """ - path = "/coverage.order" - - params: dict[str, Any] = {} - return self._client._make_request( - path, - params=params if params else None, - json_model=data, - response_model=codegen_coverage.OrderCoverageResponse, - ) - - -class Client(ClientBase): - """Synchronous client for the API.""" - - def __init__( - self, - base_url: str, - **kwargs: Any, - ) -> None: - super().__init__(base_url, **kwargs) - - self.coverage = CoverageClient(self) diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py deleted file mode 100644 index dab5fdf7..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/_rebuild.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from __future__ import annotations - - -from . import codegen_coverage - -from .codegen_coverage import ( - CodegenCoverageOrderCoverageRequest, - CodegenCoverageOrderCoverageResponse, -) - -from . import codegen_coverage - -from .codegen_coverage.coverage import ( - CodegenCoverageCoverageBaseModel, - CodegenCoverageCoverageDeepOption, - CodegenCoverageCoverageDefaultedField, - CodegenCoverageCoverageEmptyStruct, - CodegenCoverageCoverageGenericTree, - CodegenCoverageCoverageIntKeyedMap, - CodegenCoverageCoverageKeywordVariants, - CodegenCoverageCoverageKeywordVariantsClass, - CodegenCoverageCoverageKeywordVariantsLambda, - CodegenCoverageCoverageKeywordVariantsReturn, - CodegenCoverageCoverageMutualA, - CodegenCoverageCoverageMutualB, - CodegenCoverageCoveragePyKeywordFields, - CodegenCoverageCoveragePydanticReservedFields, - CodegenCoverageCoverageRequest, - CodegenCoverageCoverageResponse, - CodegenCoverageCoverageShadowingFields, - CodegenCoverageCoverageTreeNode, - CodegenCoverageCoverageWeirdDocstring, - CodegenCoverageCoverageWrapper, -) - -from . import codegen_coverage - -from .codegen_coverage.order import ( - CodegenCoverageOrderInsertData, - CodegenCoverageOrderPolicy, - CodegenCoverageOrderRateLimit, -) - - -def rebuild_models() -> None: - codegen_coverage.codegen_coverage = codegen_coverage - codegen_coverage.coverage.codegen_coverage = codegen_coverage - codegen_coverage.order.codegen_coverage = codegen_coverage - errors: list[str] = [] - for _model in [ - CodegenCoverageOrderCoverageRequest, - CodegenCoverageOrderCoverageResponse, - CodegenCoverageCoverageBaseModel, - CodegenCoverageCoverageRequest, - CodegenCoverageCoverageResponse, - CodegenCoverageCoverageDeepOption, - CodegenCoverageCoverageDefaultedField, - CodegenCoverageCoverageEmptyStruct, - CodegenCoverageCoverageGenericTree, - CodegenCoverageCoverageIntKeyedMap, - CodegenCoverageCoverageKeywordVariants, - CodegenCoverageCoverageMutualA, - CodegenCoverageCoverageMutualB, - CodegenCoverageCoveragePyKeywordFields, - CodegenCoverageCoveragePydanticReservedFields, - CodegenCoverageCoverageShadowingFields, - CodegenCoverageCoverageTreeNode, - CodegenCoverageCoverageWeirdDocstring, - CodegenCoverageCoverageWrapper, - CodegenCoverageOrderInsertData, - CodegenCoverageOrderPolicy, - CodegenCoverageOrderRateLimit, - ]: - if not hasattr(_model, "model_rebuild"): - continue - try: - _model.model_rebuild() - except Exception as exc: - errors.append(f" - {_model.__name__}: {type(exc).__name__}: {exc}") - if errors: - raise RuntimeError( - "reflectapi: failed to rebuild " - + str(len(errors)) - + " generated model(s). This usually means the codegen emitted an annotation pointing at a symbol that was never defined (a dangling type reference). Fix the codegen rather than catching this error.\n" - + "\n".join(errors) - ) diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py deleted file mode 100644 index 3679a2b2..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/__init__.py +++ /dev/null @@ -1,81 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from __future__ import annotations - - -# Standard library imports -from enum import Enum -from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union - -# Third-party imports -from pydantic import BaseModel, ConfigDict, Field, RootModel - -# Runtime imports -from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration -from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible -from reflectapi_runtime import ReflectapiPartialModel - - -# Type variables for generic types - - -C = TypeVar("C") - -T = TypeVar("T") - - -# External type definitions -StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] -StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] -StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] -StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] - - -class CodegenCoverageOrderCoverageRequest(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - order: codegen_coverage.order.OrderInsertData - rate_limit: codegen_coverage.order.RateLimit - policy: codegen_coverage.order.Policy[str, int] - - -class CodegenCoverageOrderCoverageResponse(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - ok: bool - - -# Public aliases for this module -OrderCoverageRequest = CodegenCoverageOrderCoverageRequest -OrderCoverageResponse = CodegenCoverageOrderCoverageResponse - -from . import coverage -from . import order - -try: - from .._rebuild import rebuild_models as _rebuild_models - - _rebuild_models() -except ImportError: - pass - -__all__ = [ - "CodegenCoverageOrderCoverageRequest", - "CodegenCoverageOrderCoverageResponse", - "OrderCoverageRequest", - "OrderCoverageResponse", - "coverage", - "order", -] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py deleted file mode 100644 index b2e0680e..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/coverage/__init__.py +++ /dev/null @@ -1,329 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from __future__ import annotations - - -# Standard library imports -from enum import Enum -from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union - -# Third-party imports -from pydantic import BaseModel, ConfigDict, Field, RootModel - -# Runtime imports -from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration -from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible -from reflectapi_runtime import ReflectapiPartialModel - - -# Type variables for generic types - - -C = TypeVar("C") - -T = TypeVar("T") - - -# External type definitions -StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] -StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] -StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] -StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] - - -class CodegenCoverageCoverageBaseModel(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - label: str - - -class CodegenCoverageCoverageRequest(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - keywords: codegen_coverage.coverage.PyKeywordFields - reserved: codegen_coverage.coverage.PydanticReservedFields - tree: codegen_coverage.coverage.TreeNode - mutual: codegen_coverage.coverage.MutualA - generic_tree: codegen_coverage.coverage.GenericTree[int] - keyword_variants: codegen_coverage.coverage.KeywordVariants - int_keyed: codegen_coverage.coverage.IntKeyedMap - user_id: int - deep_option: codegen_coverage.coverage.DeepOption - shadowing: codegen_coverage.coverage.ShadowingFields - empty: codegen_coverage.coverage.EmptyStruct - weird_doc: codegen_coverage.coverage.WeirdDocstring - shadow_base_model: codegen_coverage.coverage.BaseModel - wrapper_int: codegen_coverage.coverage.Wrapper[int] - wrapper_str: codegen_coverage.coverage.Wrapper[str] - defaulted: codegen_coverage.coverage.DefaultedField - - -class CodegenCoverageCoverageResponse(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - ok: bool - - -class CodegenCoverageCoverageDeepOption(ReflectapiPartialModel): - model_config = ConfigDict( - extra="ignore", - populate_by_name=True, - validate_assignment=True, - protected_namespaces=(), - ) - - maybe_maybe: str | None | None = None - - -class CodegenCoverageCoverageDefaultedField(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - count: int | None = None - - -class CodegenCoverageCoverageEmptyStruct(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - -class CodegenCoverageCoverageGenericTree(BaseModel, Generic[T]): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: T - children: list[codegen_coverage.coverage.GenericTree[T]] - - -class CodegenCoverageCoverageIntKeyedMap(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - by_id: dict[int, str] - uuid_keyed: dict[str, str] - - -class CodegenCoverageCoverageMutualA(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - name: str - b: codegen_coverage.coverage.MutualB | None = None - - -class CodegenCoverageCoverageMutualB(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - name: str - a: codegen_coverage.coverage.MutualA | None = None - - -class CodegenCoverageCoveragePyKeywordFields(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - class_: str = Field(serialization_alias="class", validation_alias="class") - from_: int = Field(serialization_alias="from", validation_alias="from") - import_: bool = Field(serialization_alias="import", validation_alias="import") - lambda_: int = Field(serialization_alias="lambda", validation_alias="lambda") - return_: list[int] = Field(serialization_alias="return", validation_alias="return") - yield_: str | None = Field( - default=None, serialization_alias="yield", validation_alias="yield" - ) - none: int | None = Field( - default=None, serialization_alias="None", validation_alias="None" - ) - true: bool = Field(serialization_alias="True", validation_alias="True") - type_: str = Field(serialization_alias="type", validation_alias="type") - match: str - - -class CodegenCoverageCoveragePydanticReservedFields(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - model_config_: str = Field( - serialization_alias="model_config", validation_alias="model_config" - ) - model_fields_set_: str = Field( - serialization_alias="model_fields_set", validation_alias="model_fields_set" - ) - model_dump_json_: str = Field( - serialization_alias="model_dump_json", validation_alias="model_dump_json" - ) - - -class CodegenCoverageCoverageShadowingFields(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - field: str - annotated: str - generic: str - base_model: str - - -class CodegenCoverageCoverageTreeNode(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: str - children: list[codegen_coverage.coverage.TreeNode] - parent: codegen_coverage.coverage.TreeNode | None = None - - -class CodegenCoverageCoverageWeirdDocstring(BaseModel): - """A docstring with "quotes" and 'apostrophes' and a backslash: \\\\ -And a \"\"\"triple quote\"\"\" inside.""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - value: str - mixed_quotes: str = Field( - description="Field description with \"double quotes\" and 'single quotes'." - ) - doubles_only: str = Field( - description='Field description with only "double quotes" — should use single-quoted Python literal.' - ) - - -class CodegenCoverageCoverageWrapper(BaseModel, Generic[T]): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - inner: T - - -class CodegenCoverageCoverageKeywordVariantsClass(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - kind: Literal["class"] = Field(default="class", description="Discriminator field") - - -class CodegenCoverageCoverageKeywordVariantsLambda(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - kind: Literal["lambda"] = Field(default="lambda", description="Discriminator field") - name: str - - -class CodegenCoverageCoverageKeywordVariantsReturn(BaseModel): - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - kind: Literal["return"] = Field(default="return", description="Discriminator field") - value: int - - -class CodegenCoverageCoverageKeywordVariants(RootModel): - root: Annotated[ - Union[ - CodegenCoverageCoverageKeywordVariantsClass, - CodegenCoverageCoverageKeywordVariantsLambda, - CodegenCoverageCoverageKeywordVariantsReturn, - ], - Field(discriminator="kind"), - ] - - -# Public aliases for this module -BaseModel = CodegenCoverageCoverageBaseModel -CoverageRequest = CodegenCoverageCoverageRequest -CoverageResponse = CodegenCoverageCoverageResponse -DeepOption = CodegenCoverageCoverageDeepOption -DefaultedField = CodegenCoverageCoverageDefaultedField -EmptyStruct = CodegenCoverageCoverageEmptyStruct -GenericTree = CodegenCoverageCoverageGenericTree -IntKeyedMap = CodegenCoverageCoverageIntKeyedMap -KeywordVariants = CodegenCoverageCoverageKeywordVariants -KeywordVariantsClass = CodegenCoverageCoverageKeywordVariantsClass -KeywordVariantsLambda = CodegenCoverageCoverageKeywordVariantsLambda -KeywordVariantsReturn = CodegenCoverageCoverageKeywordVariantsReturn -MutualA = CodegenCoverageCoverageMutualA -MutualB = CodegenCoverageCoverageMutualB -PyKeywordFields = CodegenCoverageCoveragePyKeywordFields -PydanticReservedFields = CodegenCoverageCoveragePydanticReservedFields -Request = CodegenCoverageCoverageRequest -Response = CodegenCoverageCoverageResponse -ShadowingFields = CodegenCoverageCoverageShadowingFields -TreeNode = CodegenCoverageCoverageTreeNode -WeirdDocstring = CodegenCoverageCoverageWeirdDocstring -Wrapper = CodegenCoverageCoverageWrapper - -__all__ = [ - "BaseModel", - "CodegenCoverageCoverageBaseModel", - "CodegenCoverageCoverageDeepOption", - "CodegenCoverageCoverageDefaultedField", - "CodegenCoverageCoverageEmptyStruct", - "CodegenCoverageCoverageGenericTree", - "CodegenCoverageCoverageIntKeyedMap", - "CodegenCoverageCoverageKeywordVariants", - "CodegenCoverageCoverageKeywordVariantsClass", - "CodegenCoverageCoverageKeywordVariantsLambda", - "CodegenCoverageCoverageKeywordVariantsReturn", - "CodegenCoverageCoverageMutualA", - "CodegenCoverageCoverageMutualB", - "CodegenCoverageCoveragePyKeywordFields", - "CodegenCoverageCoveragePydanticReservedFields", - "CodegenCoverageCoverageRequest", - "CodegenCoverageCoverageResponse", - "CodegenCoverageCoverageShadowingFields", - "CodegenCoverageCoverageTreeNode", - "CodegenCoverageCoverageWeirdDocstring", - "CodegenCoverageCoverageWrapper", - "CoverageRequest", - "CoverageResponse", - "DeepOption", - "DefaultedField", - "EmptyStruct", - "GenericTree", - "IntKeyedMap", - "KeywordVariants", - "KeywordVariantsClass", - "KeywordVariantsLambda", - "KeywordVariantsReturn", - "MutualA", - "MutualB", - "PyKeywordFields", - "PydanticReservedFields", - "Request", - "Response", - "ShadowingFields", - "TreeNode", - "WeirdDocstring", - "Wrapper", -] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py deleted file mode 100644 index 2a313c76..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/codegen_coverage/order/__init__.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from __future__ import annotations - - -# Standard library imports -from enum import Enum -from typing import Annotated, Any, Generic, Literal, Optional, TypeVar, Union - -# Third-party imports -from pydantic import BaseModel, ConfigDict, Field, RootModel - -# Runtime imports -from reflectapi_runtime import AsyncClientBase, ClientBase, ApiResponse -from reflectapi_runtime import ReflectapiDuration -from reflectapi_runtime import ReflectapiEmpty -from reflectapi_runtime import ReflectapiInfallible -from reflectapi_runtime import ReflectapiPartialModel - - -# Type variables for generic types - - -C = TypeVar("C") - -T = TypeVar("T") - - -# External type definitions -StdNumNonZeroU32 = Annotated[int, "Rust NonZero u32 type"] -StdNumNonZeroU64 = Annotated[int, "Rust NonZero u64 type"] -StdNumNonZeroI32 = Annotated[int, "Rust NonZero i32 type"] -StdNumNonZeroI64 = Annotated[int, "Rust NonZero i64 type"] - - -class CodegenCoverageOrderInsertData(BaseModel): - """A struct whose name begins with the parent namespace cap. - Exercises the namespace-alias path for both the stripped - (`order.InsertData`) and Rust-leaf (`order.OrderInsertData`) - forms.""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - identity: str - alternative_part_number: tuple[str, str] | None = Field( - default=None, description="Exercises the `tuple[A, B]` rendering for `(A, B)`." - ) - - -class CodegenCoverageOrderPolicy(BaseModel, Generic[C, T]): - """Exercises `PhantomData` elision (the field carries no wire - data and must not appear in the Python model).""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - name: str - - -class CodegenCoverageOrderRateLimit(BaseModel): - """Exercises the `std::time::Duration` ↔ `{secs, nanos}` wire - adapter.""" - - model_config = ConfigDict( - extra="ignore", populate_by_name=True, protected_namespaces=() - ) - - retry_after: ReflectapiDuration - max_wait: ReflectapiDuration | None = None - - -# Public aliases for this module -InsertData = CodegenCoverageOrderInsertData -OrderInsertData = CodegenCoverageOrderInsertData -Policy = CodegenCoverageOrderPolicy -RateLimit = CodegenCoverageOrderRateLimit - -__all__ = [ - "CodegenCoverageOrderInsertData", - "CodegenCoverageOrderPolicy", - "CodegenCoverageOrderRateLimit", - "InsertData", - "OrderInsertData", - "Policy", - "RateLimit", -] diff --git a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py b/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py deleted file mode 100644 index 5a62f8fa..00000000 --- a/reflectapi-demo/target/codegen-coverage-client/codegen_coverage_client/generated.py +++ /dev/null @@ -1,88 +0,0 @@ -""" -DO NOT MODIFY THIS FILE MANUALLY -This file was generated by reflectapi-cli - -Schema name: Codegen coverage -Internal test schema for Python codegen smoke tests -""" - -from __future__ import annotations - - -from ._client import AsyncClient - -from ._client import Client - -from . import codegen_coverage - -from .codegen_coverage import ( - CodegenCoverageOrderCoverageRequest, - CodegenCoverageOrderCoverageResponse, -) - -from . import codegen_coverage - -from .codegen_coverage.coverage import ( - CodegenCoverageCoverageBaseModel, - CodegenCoverageCoverageDeepOption, - CodegenCoverageCoverageDefaultedField, - CodegenCoverageCoverageEmptyStruct, - CodegenCoverageCoverageGenericTree, - CodegenCoverageCoverageIntKeyedMap, - CodegenCoverageCoverageKeywordVariants, - CodegenCoverageCoverageKeywordVariantsClass, - CodegenCoverageCoverageKeywordVariantsLambda, - CodegenCoverageCoverageKeywordVariantsReturn, - CodegenCoverageCoverageMutualA, - CodegenCoverageCoverageMutualB, - CodegenCoverageCoveragePyKeywordFields, - CodegenCoverageCoveragePydanticReservedFields, - CodegenCoverageCoverageRequest, - CodegenCoverageCoverageResponse, - CodegenCoverageCoverageShadowingFields, - CodegenCoverageCoverageTreeNode, - CodegenCoverageCoverageWeirdDocstring, - CodegenCoverageCoverageWrapper, -) - -from . import codegen_coverage - -from .codegen_coverage.order import ( - CodegenCoverageOrderInsertData, - CodegenCoverageOrderPolicy, - CodegenCoverageOrderRateLimit, -) - -from ._rebuild import rebuild_models as _rebuild_models - -_rebuild_models() - -__all__ = [ - "AsyncClient", - "Client", - "CodegenCoverageCoverageBaseModel", - "CodegenCoverageCoverageDeepOption", - "CodegenCoverageCoverageDefaultedField", - "CodegenCoverageCoverageEmptyStruct", - "CodegenCoverageCoverageGenericTree", - "CodegenCoverageCoverageIntKeyedMap", - "CodegenCoverageCoverageKeywordVariants", - "CodegenCoverageCoverageKeywordVariantsClass", - "CodegenCoverageCoverageKeywordVariantsLambda", - "CodegenCoverageCoverageKeywordVariantsReturn", - "CodegenCoverageCoverageMutualA", - "CodegenCoverageCoverageMutualB", - "CodegenCoverageCoveragePyKeywordFields", - "CodegenCoverageCoveragePydanticReservedFields", - "CodegenCoverageCoverageRequest", - "CodegenCoverageCoverageResponse", - "CodegenCoverageCoverageShadowingFields", - "CodegenCoverageCoverageTreeNode", - "CodegenCoverageCoverageWeirdDocstring", - "CodegenCoverageCoverageWrapper", - "CodegenCoverageOrderCoverageRequest", - "CodegenCoverageOrderCoverageResponse", - "CodegenCoverageOrderInsertData", - "CodegenCoverageOrderPolicy", - "CodegenCoverageOrderRateLimit", -] diff --git a/reflectapi-demo/tests/codegen_coverage.rs b/reflectapi-demo/tests/codegen_coverage.rs index fd875a44..6a3a96ee 100644 --- a/reflectapi-demo/tests/codegen_coverage.rs +++ b/reflectapi-demo/tests/codegen_coverage.rs @@ -1,15 +1,17 @@ -//! Codegen coverage fixtures + smoke test. +//! Codegen coverage fixtures for the Python client generator. //! -//! Every type in this file exercises a specific Python codegen -//! rendering path. They live here as an integration test (rather -//! than in the demo's public schema) so the demo stays a clean -//! example and the fixtures stay clearly test-only. +//! Each type below targets one rendering path: namespace alias +//! resolution, tuple types, Duration serialisation, PhantomData +//! elision, Python keyword field names, recursive and mutually +//! recursive types, and so on. //! -//! The `#[test]` at the bottom builds a private schema from these -//! fixtures, runs the Python codegen against it, and writes the -//! result to `target/codegen-coverage-client/`. CI imports that -//! generated package — the strict `_rebuild_models()` raises on any -//! dangling type reference. +//! Running this test as `cargo test -p reflectapi-demo --test +//! codegen_coverage` builds a schema from the fixtures, runs the +//! Python codegen, and writes the result to +//! `target/codegen-coverage-client/`. CI imports the generated +//! package; `_rebuild_models()` raises if any annotation points at +//! a symbol the codegen never defined, so the import step is the +//! actual assertion. use std::sync::Arc; diff --git a/reflectapi-python-runtime/src/reflectapi_runtime/client.py b/reflectapi-python-runtime/src/reflectapi_runtime/client.py index d3cf009b..efb448b0 100644 --- a/reflectapi-python-runtime/src/reflectapi_runtime/client.py +++ b/reflectapi-python-runtime/src/reflectapi_runtime/client.py @@ -259,10 +259,9 @@ def _serialize_request_body( the wire (they encode the protocol's "explicit null" state). Plain Pydantic models use ``exclude_none=True`` so an unset - optional field renders as an absent key rather than a JSON - ``null`` — matching what serde sees as - ``#[serde(skip_serializing_if = "Option::is_none")]`` and - preserving the historical wire format. + optional field renders as an absent key, matching the wire + shape produced by serde with + ``#[serde(skip_serializing_if = "Option::is_none")]``. """ # Handle primitive types (for untagged unions) if not hasattr(json_model, "model_dump_json"): @@ -822,10 +821,9 @@ def _serialize_request_body( the wire (they encode the protocol's "explicit null" state). Plain Pydantic models use ``exclude_none=True`` so an unset - optional field renders as an absent key rather than a JSON - ``null`` — matching what serde sees as - ``#[serde(skip_serializing_if = "Option::is_none")]`` and - preserving the historical wire format. + optional field renders as an absent key, matching the wire + shape produced by serde with + ``#[serde(skip_serializing_if = "Option::is_none")]``. """ # Handle primitive types (for untagged unions) if not hasattr(json_model, "model_dump_json"): From 700f7c00aa7675c7b5fc4933ca7374afae27512b Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Wed, 13 May 2026 16:21:12 +1200 Subject: [PATCH 17/17] docs: changelog entry for the Python codegen overhaul + TS SSE flush --- CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9555391..6016cfce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,56 @@ # Changelog +## Unreleased + +### Python — partial fields without a wrapper class + +The hand-rolled `ReflectapiOption[T]` runtime class is gone. Generated clients now express `reflectapi::Option` fields as plain `T | None` on a `ReflectapiPartialModel` base class, with the absent-vs-null distinction carried by Pydantic's `model_fields_set`: + +```python +# Field omitted from the wire if you don't pass it +Item(name="rex").model_dump_json() == '{"name":"rex"}' + +# Null on the wire if you pass None explicitly +Item(name="rex", kind=None).model_dump_json() == '{"name":"rex","kind":null}' + +# "Was this field on the wire?" — analogue of TypeScript `obj.kind !== undefined` +"kind" in item.model_fields_set +``` + +Migration for hand-written consumer code that touched the wrapper API: + +| Before | After | +|---|---| +| `ReflectapiOption(Undefined)` | don't set the field | +| `ReflectapiOption(None)` | `field=None` | +| `ReflectapiOption(value)` | `field=value` | +| `opt.is_undefined` | `"field" not in model.model_fields_set` | +| `opt.is_none` | `model.field is None and "field" in model.model_fields_set` | +| `opt.unwrap_or(default)` | `model.field if "field" in model.model_fields_set else default` | + +The runtime drops `ReflectapiOption`, `Undefined`, `Option`, `some`, `none`, `undefined`, and `serialize_option_dict`. `ReflectapiDuration` is added as the wire-format adapter for `std::time::Duration` (`{secs, nanos}` ↔ `timedelta`). + +Other Python codegen fixes in the same pass: + +- `Vec` renders as `list[int]` to match serde's default JSON shape (was `bytes`, which Pydantic rejected against the wire form). +- `PhantomData` fields are dropped from generated models (Rust-only type marker, no wire data). +- `std::tuple::TupleN` types render as Python `tuple[…]`. +- Field names that exact-match Pydantic methods (`model_dump`, `schema`, `json`, …) are renamed with a trailing underscore and aliased to the wire name; `model_*` field prefixes work without warnings via `protected_namespaces=()`. +- `_rebuild_models()` raises a structured `RuntimeError` listing every model whose annotations don't resolve, instead of silently swallowing the failures. + +### TypeScript — SSE final-event flush + +The generated `__EventSourceParserStream` gains a `flush()` that feeds `'\n\n'` to the parser on stream close. Without it, a server that closes the connection immediately after the final `data:` line — without the spec's trailing blank-line terminator — silently dropped the last event. Regenerated clients pick up the fix. + +### Rust + +No generated-code changes. + +### Internals + +- OpenAPI codegen now tracks in-progress conversions by full monomorphization, so generic self-referential types no longer recurse forever. +- `reflectapi-derive` reads `#[doc]` literals via `LitStr::value()` so doc comments with literal quotes land in the schema without source-level `\"` escaping. + ## 0.17.2 — transport-shape refactor The Rust, TypeScript, and Python clients now share a single `Request` DTO shape: `{ path, headers, body }`. The base URL lives on the transport, not on the generated `Interface`.