diff --git a/README.md b/README.md index b1266f8a..002632a2 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,13 @@ Options: generating models. --include-request-argument Auto-inject a FastAPI Request parameter into operations when not present. + --allow-remote-refs / --no-allow-remote-refs + Allow or block fetching remote $ref targets + over HTTP/HTTPS. The default follows + datamodel-code-generator compatibility + behavior. + --allow-private-network Allow trusted remote $ref targets on local + or private network addresses. -d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct] [default: pydantic_v2.BaseModel] -p, --python-version [3.10|3.11|3.12|3.13|3.14] diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 321c492c..ff6bf4bc 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -22,6 +22,13 @@ Options: generating models. --include-request-argument Auto-inject a FastAPI Request parameter into operations when not present. + --allow-remote-refs / --no-allow-remote-refs + Allow or block fetching remote $ref targets + over HTTP/HTTPS. The default follows + datamodel-code-generator compatibility + behavior. + --allow-private-network Allow trusted remote $ref targets on local + or private network addresses. -d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct] [default: pydantic_v2.BaseModel] -p, --python-version [3.10|3.11|3.12|3.13|3.14] @@ -81,6 +88,26 @@ Render generated files with a custom template directory. Input schema: `openapi/custom_template_security/custom_security.yaml` +### --allow-private-network + +Allow trusted remote `$ref` targets on local or private network addresses. + +`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-private-network` + +Input schema: `openapi/remote_ref/body_and_parameters.yaml` + +Related options: `--allow-remote-refs` + +### --allow-remote-refs, --no-allow-remote-refs + +Allow or block fetching remote `$ref` targets over HTTP/HTTPS. + +`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-remote-refs` + +Input schema: `openapi/remote_ref/body_and_parameters.yaml` + +Related options: `--allow-private-network` + ### --include-request-argument Auto-inject a FastAPI Request argument in generated operation signatures when not present. diff --git a/docs/index.md b/docs/index.md index f8d59598..e0914ebf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -61,6 +61,13 @@ Options: generating models. --include-request-argument Auto-inject a FastAPI Request parameter into operations when not present. + --allow-remote-refs / --no-allow-remote-refs + Allow or block fetching remote $ref targets + over HTTP/HTTPS. The default follows + datamodel-code-generator compatibility + behavior. + --allow-private-network Allow trusted remote $ref targets on local + or private network addresses. -d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct] [default: pydantic_v2.BaseModel] -p, --python-version [3.10|3.11|3.12|3.13|3.14] diff --git a/docs/llms-full.txt b/docs/llms-full.txt index f1acf99b..c15d4e04 100644 --- a/docs/llms-full.txt +++ b/docs/llms-full.txt @@ -63,6 +63,13 @@ Options: generating models. --include-request-argument Auto-inject a FastAPI Request parameter into operations when not present. + --allow-remote-refs / --no-allow-remote-refs + Allow or block fetching remote $ref targets + over HTTP/HTTPS. The default follows + datamodel-code-generator compatibility + behavior. + --allow-private-network Allow trusted remote $ref targets on local + or private network addresses. -d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct] [default: pydantic_v2.BaseModel] -p, --python-version [3.10|3.11|3.12|3.13|3.14] @@ -352,6 +359,13 @@ Options: generating models. --include-request-argument Auto-inject a FastAPI Request parameter into operations when not present. + --allow-remote-refs / --no-allow-remote-refs + Allow or block fetching remote $ref targets + over HTTP/HTTPS. The default follows + datamodel-code-generator compatibility + behavior. + --allow-private-network Allow trusted remote $ref targets on local + or private network addresses. -d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct] [default: pydantic_v2.BaseModel] -p, --python-version [3.10|3.11|3.12|3.13|3.14] @@ -411,6 +425,26 @@ Render generated files with a custom template directory. Input schema: `openapi/custom_template_security/custom_security.yaml` +### --allow-private-network + +Allow trusted remote `$ref` targets on local or private network addresses. + +`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-private-network` + +Input schema: `openapi/remote_ref/body_and_parameters.yaml` + +Related options: `--allow-remote-refs` + +### --allow-remote-refs, --no-allow-remote-refs + +Allow or block fetching remote `$ref` targets over HTTP/HTTPS. + +`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-remote-refs` + +Input schema: `openapi/remote_ref/body_and_parameters.yaml` + +Related options: `--allow-private-network` + ### --include-request-argument Auto-inject a FastAPI Request argument in generated operation signatures when not present. diff --git a/fastapi_code_generator/_types/generate_config_dict.py b/fastapi_code_generator/_types/generate_config_dict.py index f274dad2..09574108 100644 --- a/fastapi_code_generator/_types/generate_config_dict.py +++ b/fastapi_code_generator/_types/generate_config_dict.py @@ -9,6 +9,8 @@ class GenerateConfigDict(TypedDict): + allow_private_network: NotRequired[bool] + allow_remote_refs: NotRequired[bool | None] custom_visitors: NotRequired[list[str] | None] disable_timestamp: NotRequired[bool] enable_faux_immutability: NotRequired[bool] diff --git a/fastapi_code_generator/cli.py b/fastapi_code_generator/cli.py index 559cfdf9..377527b1 100644 --- a/fastapi_code_generator/cli.py +++ b/fastapi_code_generator/cli.py @@ -73,6 +73,25 @@ def _show_version(value: bool) -> None: raise typer.Exit() +def _resolve_remote_reference_options( + allow_remote_refs: Optional[bool], allow_private_network: bool +) -> tuple[Optional[bool], bool]: + match allow_remote_refs, allow_private_network: + case False, True: + return False, False + case None, True: + return True, True + case _: + pass + return allow_remote_refs, allow_private_network + + +def _parse_specified_tags(specify_tags: Optional[str]) -> set[str]: + if not specify_tags: + return set() + return {tag for raw_tag in specify_tags.split(",") if (tag := raw_tag.strip())} + + @lru_cache(maxsize=1) def _get_command() -> Command: return get_command(app) @@ -108,6 +127,22 @@ def main( "present." ), ), + allow_remote_refs: Optional[bool] = typer.Option( + None, + "--allow-remote-refs/--no-allow-remote-refs", + help=( + "Allow or block fetching remote $ref targets over HTTP/HTTPS. " + "The default follows datamodel-code-generator compatibility behavior." + ), + ), + allow_private_network: bool = typer.Option( + False, + "--allow-private-network", + help=( + "Allow trusted remote $ref targets on local or private network " + "addresses." + ), + ), output_model_type: DataModelType = typer.Option( DataModelType.PydanticV2BaseModel.value, "--output-model-type", "-d" ), @@ -162,6 +197,8 @@ def main( disable_timestamp=disable_timestamp, strict_nullable=strict_nullable, include_request_argument=include_request_argument, + allow_remote_refs=allow_remote_refs, + allow_private_network=allow_private_network, generate_routers=generate_routers, specify_tags=specify_tags, output_model_type=output_model_type, @@ -203,6 +240,8 @@ def generate_code( disable_timestamp: bool = False, strict_nullable: bool = False, include_request_argument: bool = False, + allow_remote_refs: Optional[bool] = None, + allow_private_network: bool = False, generate_routers: Optional[bool] = None, specify_tags: Optional[str] = None, output_model_type: DataModelType = DataModelType.PydanticV2BaseModel, @@ -226,6 +265,9 @@ def generate_code( custom_visitors = [] data_model_types = get_data_model_types(output_model_type, python_version) code_formatter = _get_code_formatter(python_version, Path().resolve()) + allow_remote_refs, allow_private_network = _resolve_remote_reference_options( + allow_remote_refs, allow_private_network + ) parser = OpenAPIParser( input_text, @@ -239,6 +281,8 @@ def generate_code( target_python_version=python_version, strict_nullable=strict_nullable, include_request_argument=include_request_argument, + allow_remote_refs=allow_remote_refs, + allow_private_network=allow_private_network, use_annotated=use_annotated, reuse_model=reuse_model, enable_faux_immutability=enable_faux_immutability, @@ -304,9 +348,7 @@ def generate_code( specified_tags = set() existing_main_has_router_includes = False if generate_routers and specify_tags: - specified_tags = { - tag.strip() for tag in str(specify_tags).split(",") if tag.strip() - } + specified_tags = _parse_specified_tags(specify_tags) main_path = output_dir / "main.py" if main_path.exists(): existing_main_has_router_includes = ( diff --git a/fastapi_code_generator/config.py b/fastapi_code_generator/config.py index 11e94766..1dc3f003 100644 --- a/fastapi_code_generator/config.py +++ b/fastapi_code_generator/config.py @@ -163,6 +163,21 @@ class GenerateConfig(BaseModel): ), json_schema_extra=cast(Any, _cli_metadata("--include-request-argument")), ) + allow_remote_refs: bool | None = Field( + default=None, + description="Allow or block fetching remote `$ref` targets over HTTP/HTTPS.", + json_schema_extra=cast( + Any, _cli_metadata("--allow-remote-refs", "--no-allow-remote-refs") + ), + ) + allow_private_network: bool = Field( + default=False, + description=( + "Allow trusted remote `$ref` targets on local or private network " + "addresses." + ), + json_schema_extra=cast(Any, _cli_metadata("--allow-private-network")), + ) output_model_type: OutputModelTypeName = Field( default="pydantic_v2.BaseModel", description="Model backend passed through to datamodel-code-generator.", @@ -296,9 +311,10 @@ def validate_generate_config_model() -> None: cli = _get_cli_metadata(field) param = params[name] expected_flags = tuple(cli["flags"]) - if tuple(param.opts) != expected_flags: + actual_flags = tuple(param.opts) + tuple(getattr(param, "secondary_opts", ())) + if actual_flags != expected_flags: raise ValueError( - f"{name} flags drifted: expected {expected_flags!r}, got {tuple(param.opts)!r}" + f"{name} flags drifted: expected {expected_flags!r}, got {actual_flags!r}" ) if cli["multiple"] != bool(getattr(param, "multiple", False)): raise ValueError(f"{name} multiple setting drifted from the CLI") diff --git a/fastapi_code_generator/parser.py b/fastapi_code_generator/parser.py index 947d460f..baae186a 100644 --- a/fastapi_code_generator/parser.py +++ b/fastapi_code_generator/parser.py @@ -347,6 +347,8 @@ def __init__( field_extra_keys: Optional[Set[str]] = None, field_include_all_keys: bool = False, include_request_argument: bool = False, + allow_remote_refs: Optional[bool] = None, + allow_private_network: bool = False, use_annotated: bool = False, ): super().__init__( @@ -389,6 +391,8 @@ def __init__( field_extra_keys=field_extra_keys, field_include_all_keys=field_include_all_keys, openapi_scopes=[OpenAPIScope.Schemas, OpenAPIScope.Paths], + allow_remote_refs=allow_remote_refs, + allow_private_network=allow_private_network, use_annotated=use_annotated, ) self.operations: Dict[str, Operation] = {} @@ -438,12 +442,17 @@ def get_parameter_type( schema: Optional[JsonSchemaObject] = None data_type: Optional[DataType] = None for content in parameters.content.values(): - if isinstance(content.schema_, ReferenceObject): - data_type = self.get_ref_data_type(content.schema_.ref) - ref_model = self.get_ref_model(content.schema_.ref) - schema = JsonSchemaObject.model_validate(ref_model) # pragma: no cover - else: - schema = content.schema_ + match content.schema_: + case ReferenceObject() as schema_ref: + data_type = self.get_ref_data_type(schema_ref.ref) + ref_model = self.get_ref_model(schema_ref.ref) + schema = JsonSchemaObject.model_validate( + ref_model + ) # pragma: no cover + case JsonSchemaObject() as schema_obj: + schema = schema_obj + case _: + continue break if not data_type: if not schema: diff --git a/fastapi_code_generator/prompt_data.py b/fastapi_code_generator/prompt_data.py index 936c1a2e..2db84c39 100644 --- a/fastapi_code_generator/prompt_data.py +++ b/fastapi_code_generator/prompt_data.py @@ -143,6 +143,28 @@ 'type': 'boolean', 'choices': [], }, + { + 'name': 'allow_remote_refs', + 'cli_flags': ['--allow-remote-refs', '--no-allow-remote-refs'], + 'description': 'Allow or block fetching remote `$ref` targets ' + 'over HTTP/HTTPS.', + 'required': False, + 'default': None, + 'multiple': False, + 'type': 'boolean | null', + 'choices': [], + }, + { + 'name': 'allow_private_network', + 'cli_flags': ['--allow-private-network'], + 'description': 'Allow trusted remote `$ref` targets on local or ' + 'private network addresses.', + 'required': False, + 'default': False, + 'multiple': False, + 'type': 'boolean', + 'choices': [], + }, { 'name': 'output_model_type', 'cli_flags': ['--output-model-type', '-d'], @@ -255,6 +277,32 @@ ], 'input_schema': 'openapi/custom_template_security/custom_security.yaml', }, + { + 'options': ['--allow-private-network'], + 'description': 'Allow trusted remote `$ref` targets on local or ' + 'private network addresses.', + 'cli_args': [ + '--input', + 'openapi/remote_ref/body_and_parameters.yaml', + '--output', + 'app', + '--allow-private-network', + ], + 'input_schema': 'openapi/remote_ref/body_and_parameters.yaml', + }, + { + 'options': ['--allow-remote-refs', '--no-allow-remote-refs'], + 'description': 'Allow or block fetching remote `$ref` targets over ' + 'HTTP/HTTPS.', + 'cli_args': [ + '--input', + 'openapi/remote_ref/body_and_parameters.yaml', + '--output', + 'app', + '--allow-remote-refs', + ], + 'input_schema': 'openapi/remote_ref/body_and_parameters.yaml', + }, { 'options': ['--include-request-argument'], 'description': 'Auto-inject a FastAPI Request argument in generated ' diff --git a/pyproject.toml b/pyproject.toml index 7a7ee785..bd5f2552 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ classifiers = [ dynamic = ["version"] dependencies = [ "click>=8.1.8,<8.2", - "datamodel-code-generator[http]>=0.59,<0.60", + "datamodel-code-generator[http]>=0.61,<0.66", "jinja2>=2.11.2,<4", "pydantic>=2.12,<3", "pysnooper>=0.4.1,<2", diff --git a/tests/cli_doc/test_cli_doc_coverage.py b/tests/cli_doc/test_cli_doc_coverage.py index 25646ccc..24293f2c 100644 --- a/tests/cli_doc/test_cli_doc_coverage.py +++ b/tests/cli_doc/test_cli_doc_coverage.py @@ -29,13 +29,23 @@ def get_all_canonical_options() -> set[str]: return options +def iter_canonical_marker_options(marker_options: list[str]) -> set[str]: + if any(option.startswith("--no-") for option in marker_options): + return {get_canonical_option(marker_options)} + return {get_canonical_option([option]) for option in marker_options} + + @pytest.fixture(scope="module") def collected_options(request: pytest.FixtureRequest) -> set[str]: items: list[dict[str, Any]] = getattr(request.config, "_cli_doc_items", []) options: set[str] = set() for item in items: - options.update(item.get("marker_kwargs", {}).get("options", [])) - return {get_canonical_option([option]) for option in options} + options.update( + iter_canonical_marker_options( + item.get("marker_kwargs", {}).get("options", []) + ) + ) + return options def test_all_options_have_cli_doc_markers(collected_options: set[str]) -> None: diff --git a/tests/cli_doc/test_cli_options_sync.py b/tests/cli_doc/test_cli_options_sync.py index 37cbcdcd..2c689292 100644 --- a/tests/cli_doc/test_cli_options_sync.py +++ b/tests/cli_doc/test_cli_options_sync.py @@ -7,7 +7,7 @@ from tests.cli_doc.test_cli_doc_coverage import ( MANUAL_DOCS, get_all_canonical_options, - get_canonical_option, + iter_canonical_marker_options, ) @@ -16,8 +16,12 @@ def collected_options(request: pytest.FixtureRequest) -> set[str]: items: list[dict[str, Any]] = getattr(request.config, "_cli_doc_items", []) options: set[str] = set() for item in items: - options.update(item.get("marker_kwargs", {}).get("options", [])) - return {get_canonical_option([option]) for option in options} + options.update( + iter_canonical_marker_options( + item.get("marker_kwargs", {}).get("options", []) + ) + ) + return options def test_manual_doc_options_exist_in_cli() -> None: diff --git a/tests/main/test_main.py b/tests/main/test_main.py index c1c94141..5a7af134 100644 --- a/tests/main/test_main.py +++ b/tests/main/test_main.py @@ -213,6 +213,36 @@ def serve_schema(schema_text: str) -> Iterator[str]: @freeze_time("2020-06-19") +@pytest.mark.cli_doc( + options=["--allow-private-network"], + option_description=( + "Allow trusted remote `$ref` targets on local or private network addresses." + ), + cli_args=[ + "--input", + "openapi/remote_ref/body_and_parameters.yaml", + "--output", + "app", + "--allow-private-network", + ], + input_schema="openapi/remote_ref/body_and_parameters.yaml", + golden_output="openapi/remote_ref/body_and_parameters/main.py", + related_options=["--allow-remote-refs"], +) +@pytest.mark.cli_doc( + options=["--allow-remote-refs", "--no-allow-remote-refs"], + option_description="Allow or block fetching remote `$ref` targets over HTTP/HTTPS.", + cli_args=[ + "--input", + "openapi/remote_ref/body_and_parameters.yaml", + "--output", + "app", + "--allow-remote-refs", + ], + input_schema="openapi/remote_ref/body_and_parameters.yaml", + golden_output="openapi/remote_ref/body_and_parameters/main.py", + related_options=["--allow-private-network"], +) def test_generate_remote_ref(tmp_path: Path, output_dir: Path) -> None: oas_file = DATA_PATH / OPEN_API_REMOTE_REF_DIR_NAME / "body_and_parameters.yaml" remote_schema = oas_file.read_text(encoding="utf-8") @@ -228,6 +258,7 @@ def test_generate_remote_ref(tmp_path: Path, output_dir: Path) -> None: input_path=input_path, output_path=output_dir, expected_path=EXPECTED_OPENAPI_PATH / "remote_ref" / oas_file.stem, + extra_args=["--allow-private-network"], ) diff --git a/tests/test_config.py b/tests/test_config.py index f2d607dd..c37869db 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -93,6 +93,7 @@ def _fake_params() -> dict[str, SimpleNamespace]: for name, param in get_command_config_params().items(): fake[name] = SimpleNamespace( opts=tuple(param.opts), + secondary_opts=tuple(getattr(param, "secondary_opts", ())), multiple=bool(getattr(param, "multiple", False)), required=bool(param.required), default=param.default, diff --git a/tests/test_parser.py b/tests/test_parser.py index ae2ac838..5108168e 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -2,8 +2,16 @@ import pytest from datamodel_code_generator.parser.jsonschema import JsonSchemaObject -from datamodel_code_generator.parser.openapi import ReferenceObject, RequestBodyObject +from datamodel_code_generator.parser.openapi import ( + ParameterObject, + ReferenceObject, + RequestBodyObject, +) +from fastapi_code_generator.cli import ( + _parse_specified_tags, + _resolve_remote_reference_options, +) from fastapi_code_generator.parser import OpenAPIParser, UsefulStr, snakecase @@ -44,6 +52,16 @@ def test_useful_str_case_helpers_match_legacy_stringcase_behavior( assert useful_value.pascalcase == expected_pascal +def test_cli_helpers_normalize_remote_refs_and_tags() -> None: + assert _resolve_remote_reference_options(None, True) == (True, True) + assert _resolve_remote_reference_options(False, True) == (False, False) + assert _parse_specified_tags(None) == set() + assert _parse_specified_tags(" Fat Cats, ,Wild Boars ") == { + "Fat Cats", + "Wild Boars", + } + + def assert_field_extras( parser: OpenAPIParser, schema_data: dict[str, object], expected: dict[str, object] ) -> None: @@ -165,6 +183,25 @@ def test_parse_request_body_filters_multipart_from_mixed_content() -> None: assert set(request_body_fields) == {"application/json"} +def test_get_parameter_type_uses_parameter_schema_for_boolean_content_schema() -> None: + parser = OpenAPIParser( + "openapi: 3.0.0\ninfo: {title: Test, version: '1.0'}\npaths: {}\n" + ) + parameter = ParameterObject.model_validate( + { + "name": "message", + "in": "query", + "schema": {"type": "string"}, + "content": {"application/json": {"schema": True}}, + } + ) + + argument = parser.get_parameter_type(parameter, path=["paths", "items", "get"]) + + assert argument.name == "message" + assert argument.type_hint == "Optional[str]" + + def test_get_field_extras_preserves_non_union_discriminator() -> None: parser = OpenAPIParser( "openapi: 3.0.0\ninfo: {title: Test, version: '1.0'}\npaths: {}\n" diff --git a/uv.lock b/uv.lock index 032c82d3..a3ea2399 100644 --- a/uv.lock +++ b/uv.lock @@ -563,22 +563,22 @@ wheels = [ [[package]] name = "datamodel-code-generator" -version = "0.59.0" +version = "0.65.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, - { name = "black" }, + { name = "black", marker = "sys_platform != 'emscripten'" }, { name = "genson" }, { name = "inflect" }, - { name = "isort" }, + { name = "isort", marker = "sys_platform != 'emscripten'" }, { name = "jinja2" }, { name = "pydantic" }, { name = "pyyaml" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8d/50/21488efdb515afc04039e19d876ee668d85a5c9590a3a4bb25b27f2b99c0/datamodel_code_generator-0.59.0.tar.gz", hash = "sha256:054e4d5568c27db5a993f6b3e1d34af53bd1f6d1b6c18b7166908b0f3dc04bd4", size = 1098070, upload-time = "2026-05-29T15:47:55.127Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/0e/5912b493a114591ebf66530fecda0f4a5e0e746ab7ea30f6da15de460e21/datamodel_code_generator-0.65.1.tar.gz", hash = "sha256:65471eaa32753d738f0e7ff8e0db3ac1fbc97ff64e393cd8df427eed77e86661", size = 1418449, upload-time = "2026-06-25T09:32:26.482Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/ef/8e2479e3ba432bb3333d0f01d6bdc520cc7f765ccbfac134b015d806bc2b/datamodel_code_generator-0.59.0-py3-none-any.whl", hash = "sha256:c8c119ab618d24a619d635fef7aa9c96b69e069a4d287c9adfc148ae28368a69", size = 316232, upload-time = "2026-05-29T15:47:53.228Z" }, + { url = "https://files.pythonhosted.org/packages/c7/30/c911d29d3ce3e1d3e3a4fa7dbf39b9bdef8135094dba320edb4e2b568fb5/datamodel_code_generator-0.65.1-py3-none-any.whl", hash = "sha256:ca1dd498c54679841cd0541a09edd5f2ee2021a455f225c61a38a166c0db5d22", size = 407906, upload-time = "2026-06-25T09:32:24.906Z" }, ] [package.optional-dependencies] @@ -735,7 +735,7 @@ type = [ [package.metadata] requires-dist = [ { name = "click", specifier = ">=8.1.8,<8.2" }, - { name = "datamodel-code-generator", extras = ["http"], specifier = ">=0.59,<0.60" }, + { name = "datamodel-code-generator", extras = ["http"], specifier = ">=0.61,<0.66" }, { name = "jinja2", specifier = ">=2.11.2,<4" }, { name = "pydantic", specifier = ">=2.12,<3" }, { name = "pysnooper", specifier = ">=0.4.1,<2" },