Skip to content

Commit ec5b615

Browse files
authored
Merge pull request #611 from koxudaxi/update-datamodel-code-generator
Update datamodel-code-generator requirement
2 parents a253d2a + fd73150 commit ec5b615

16 files changed

Lines changed: 299 additions & 24 deletions

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ Options:
6464
generating models.
6565
--include-request-argument Auto-inject a FastAPI Request parameter into
6666
operations when not present.
67+
--allow-remote-refs / --no-allow-remote-refs
68+
Allow or block fetching remote $ref targets
69+
over HTTP/HTTPS. The default follows
70+
datamodel-code-generator compatibility
71+
behavior.
72+
--allow-private-network Allow trusted remote $ref targets on local
73+
or private network addresses.
6774
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
6875
[default: pydantic_v2.BaseModel]
6976
-p, --python-version [3.10|3.11|3.12|3.13|3.14]

docs/cli-reference.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ Options:
2222
generating models.
2323
--include-request-argument Auto-inject a FastAPI Request parameter into
2424
operations when not present.
25+
--allow-remote-refs / --no-allow-remote-refs
26+
Allow or block fetching remote $ref targets
27+
over HTTP/HTTPS. The default follows
28+
datamodel-code-generator compatibility
29+
behavior.
30+
--allow-private-network Allow trusted remote $ref targets on local
31+
or private network addresses.
2532
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
2633
[default: pydantic_v2.BaseModel]
2734
-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.
8188

8289
Input schema: `openapi/custom_template_security/custom_security.yaml`
8390

91+
### --allow-private-network
92+
93+
Allow trusted remote `$ref` targets on local or private network addresses.
94+
95+
`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-private-network`
96+
97+
Input schema: `openapi/remote_ref/body_and_parameters.yaml`
98+
99+
Related options: `--allow-remote-refs`
100+
101+
### --allow-remote-refs, --no-allow-remote-refs
102+
103+
Allow or block fetching remote `$ref` targets over HTTP/HTTPS.
104+
105+
`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-remote-refs`
106+
107+
Input schema: `openapi/remote_ref/body_and_parameters.yaml`
108+
109+
Related options: `--allow-private-network`
110+
84111
### --include-request-argument
85112

86113
Auto-inject a FastAPI Request argument in generated operation signatures when not present.

docs/index.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ Options:
6161
generating models.
6262
--include-request-argument Auto-inject a FastAPI Request parameter into
6363
operations when not present.
64+
--allow-remote-refs / --no-allow-remote-refs
65+
Allow or block fetching remote $ref targets
66+
over HTTP/HTTPS. The default follows
67+
datamodel-code-generator compatibility
68+
behavior.
69+
--allow-private-network Allow trusted remote $ref targets on local
70+
or private network addresses.
6471
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
6572
[default: pydantic_v2.BaseModel]
6673
-p, --python-version [3.10|3.11|3.12|3.13|3.14]

docs/llms-full.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ Options:
6363
generating models.
6464
--include-request-argument Auto-inject a FastAPI Request parameter into
6565
operations when not present.
66+
--allow-remote-refs / --no-allow-remote-refs
67+
Allow or block fetching remote $ref targets
68+
over HTTP/HTTPS. The default follows
69+
datamodel-code-generator compatibility
70+
behavior.
71+
--allow-private-network Allow trusted remote $ref targets on local
72+
or private network addresses.
6673
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
6774
[default: pydantic_v2.BaseModel]
6875
-p, --python-version [3.10|3.11|3.12|3.13|3.14]
@@ -352,6 +359,13 @@ Options:
352359
generating models.
353360
--include-request-argument Auto-inject a FastAPI Request parameter into
354361
operations when not present.
362+
--allow-remote-refs / --no-allow-remote-refs
363+
Allow or block fetching remote $ref targets
364+
over HTTP/HTTPS. The default follows
365+
datamodel-code-generator compatibility
366+
behavior.
367+
--allow-private-network Allow trusted remote $ref targets on local
368+
or private network addresses.
355369
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
356370
[default: pydantic_v2.BaseModel]
357371
-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.
411425

412426
Input schema: `openapi/custom_template_security/custom_security.yaml`
413427

428+
### --allow-private-network
429+
430+
Allow trusted remote `$ref` targets on local or private network addresses.
431+
432+
`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-private-network`
433+
434+
Input schema: `openapi/remote_ref/body_and_parameters.yaml`
435+
436+
Related options: `--allow-remote-refs`
437+
438+
### --allow-remote-refs, --no-allow-remote-refs
439+
440+
Allow or block fetching remote `$ref` targets over HTTP/HTTPS.
441+
442+
`fastapi-codegen --input openapi/remote_ref/body_and_parameters.yaml --output app --allow-remote-refs`
443+
444+
Input schema: `openapi/remote_ref/body_and_parameters.yaml`
445+
446+
Related options: `--allow-private-network`
447+
414448
### --include-request-argument
415449

416450
Auto-inject a FastAPI Request argument in generated operation signatures when not present.

fastapi_code_generator/_types/generate_config_dict.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010

1111
class GenerateConfigDict(TypedDict):
12+
allow_private_network: NotRequired[bool]
13+
allow_remote_refs: NotRequired[bool | None]
1214
custom_visitors: NotRequired[list[str] | None]
1315
disable_timestamp: NotRequired[bool]
1416
enable_faux_immutability: NotRequired[bool]

fastapi_code_generator/cli.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,25 @@ def _show_version(value: bool) -> None:
7373
raise typer.Exit()
7474

7575

76+
def _resolve_remote_reference_options(
77+
allow_remote_refs: Optional[bool], allow_private_network: bool
78+
) -> tuple[Optional[bool], bool]:
79+
match allow_remote_refs, allow_private_network:
80+
case False, True:
81+
return False, False
82+
case None, True:
83+
return True, True
84+
case _:
85+
pass
86+
return allow_remote_refs, allow_private_network
87+
88+
89+
def _parse_specified_tags(specify_tags: Optional[str]) -> set[str]:
90+
if not specify_tags:
91+
return set()
92+
return {tag for raw_tag in specify_tags.split(",") if (tag := raw_tag.strip())}
93+
94+
7695
@lru_cache(maxsize=1)
7796
def _get_command() -> Command:
7897
return get_command(app)
@@ -108,6 +127,22 @@ def main(
108127
"present."
109128
),
110129
),
130+
allow_remote_refs: Optional[bool] = typer.Option(
131+
None,
132+
"--allow-remote-refs/--no-allow-remote-refs",
133+
help=(
134+
"Allow or block fetching remote $ref targets over HTTP/HTTPS. "
135+
"The default follows datamodel-code-generator compatibility behavior."
136+
),
137+
),
138+
allow_private_network: bool = typer.Option(
139+
False,
140+
"--allow-private-network",
141+
help=(
142+
"Allow trusted remote $ref targets on local or private network "
143+
"addresses."
144+
),
145+
),
111146
output_model_type: DataModelType = typer.Option(
112147
DataModelType.PydanticV2BaseModel.value, "--output-model-type", "-d"
113148
),
@@ -162,6 +197,8 @@ def main(
162197
disable_timestamp=disable_timestamp,
163198
strict_nullable=strict_nullable,
164199
include_request_argument=include_request_argument,
200+
allow_remote_refs=allow_remote_refs,
201+
allow_private_network=allow_private_network,
165202
generate_routers=generate_routers,
166203
specify_tags=specify_tags,
167204
output_model_type=output_model_type,
@@ -203,6 +240,8 @@ def generate_code(
203240
disable_timestamp: bool = False,
204241
strict_nullable: bool = False,
205242
include_request_argument: bool = False,
243+
allow_remote_refs: Optional[bool] = None,
244+
allow_private_network: bool = False,
206245
generate_routers: Optional[bool] = None,
207246
specify_tags: Optional[str] = None,
208247
output_model_type: DataModelType = DataModelType.PydanticV2BaseModel,
@@ -226,6 +265,9 @@ def generate_code(
226265
custom_visitors = []
227266
data_model_types = get_data_model_types(output_model_type, python_version)
228267
code_formatter = _get_code_formatter(python_version, Path().resolve())
268+
allow_remote_refs, allow_private_network = _resolve_remote_reference_options(
269+
allow_remote_refs, allow_private_network
270+
)
229271

230272
parser = OpenAPIParser(
231273
input_text,
@@ -239,6 +281,8 @@ def generate_code(
239281
target_python_version=python_version,
240282
strict_nullable=strict_nullable,
241283
include_request_argument=include_request_argument,
284+
allow_remote_refs=allow_remote_refs,
285+
allow_private_network=allow_private_network,
242286
use_annotated=use_annotated,
243287
reuse_model=reuse_model,
244288
enable_faux_immutability=enable_faux_immutability,
@@ -304,9 +348,7 @@ def generate_code(
304348
specified_tags = set()
305349
existing_main_has_router_includes = False
306350
if generate_routers and specify_tags:
307-
specified_tags = {
308-
tag.strip() for tag in str(specify_tags).split(",") if tag.strip()
309-
}
351+
specified_tags = _parse_specified_tags(specify_tags)
310352
main_path = output_dir / "main.py"
311353
if main_path.exists():
312354
existing_main_has_router_includes = (

fastapi_code_generator/config.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,21 @@ class GenerateConfig(BaseModel):
163163
),
164164
json_schema_extra=cast(Any, _cli_metadata("--include-request-argument")),
165165
)
166+
allow_remote_refs: bool | None = Field(
167+
default=None,
168+
description="Allow or block fetching remote `$ref` targets over HTTP/HTTPS.",
169+
json_schema_extra=cast(
170+
Any, _cli_metadata("--allow-remote-refs", "--no-allow-remote-refs")
171+
),
172+
)
173+
allow_private_network: bool = Field(
174+
default=False,
175+
description=(
176+
"Allow trusted remote `$ref` targets on local or private network "
177+
"addresses."
178+
),
179+
json_schema_extra=cast(Any, _cli_metadata("--allow-private-network")),
180+
)
166181
output_model_type: OutputModelTypeName = Field(
167182
default="pydantic_v2.BaseModel",
168183
description="Model backend passed through to datamodel-code-generator.",
@@ -296,9 +311,10 @@ def validate_generate_config_model() -> None:
296311
cli = _get_cli_metadata(field)
297312
param = params[name]
298313
expected_flags = tuple(cli["flags"])
299-
if tuple(param.opts) != expected_flags:
314+
actual_flags = tuple(param.opts) + tuple(getattr(param, "secondary_opts", ()))
315+
if actual_flags != expected_flags:
300316
raise ValueError(
301-
f"{name} flags drifted: expected {expected_flags!r}, got {tuple(param.opts)!r}"
317+
f"{name} flags drifted: expected {expected_flags!r}, got {actual_flags!r}"
302318
)
303319
if cli["multiple"] != bool(getattr(param, "multiple", False)):
304320
raise ValueError(f"{name} multiple setting drifted from the CLI")

fastapi_code_generator/parser.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,8 @@ def __init__(
347347
field_extra_keys: Optional[Set[str]] = None,
348348
field_include_all_keys: bool = False,
349349
include_request_argument: bool = False,
350+
allow_remote_refs: Optional[bool] = None,
351+
allow_private_network: bool = False,
350352
use_annotated: bool = False,
351353
):
352354
super().__init__(
@@ -389,6 +391,8 @@ def __init__(
389391
field_extra_keys=field_extra_keys,
390392
field_include_all_keys=field_include_all_keys,
391393
openapi_scopes=[OpenAPIScope.Schemas, OpenAPIScope.Paths],
394+
allow_remote_refs=allow_remote_refs,
395+
allow_private_network=allow_private_network,
392396
use_annotated=use_annotated,
393397
)
394398
self.operations: Dict[str, Operation] = {}
@@ -438,12 +442,17 @@ def get_parameter_type(
438442
schema: Optional[JsonSchemaObject] = None
439443
data_type: Optional[DataType] = None
440444
for content in parameters.content.values():
441-
if isinstance(content.schema_, ReferenceObject):
442-
data_type = self.get_ref_data_type(content.schema_.ref)
443-
ref_model = self.get_ref_model(content.schema_.ref)
444-
schema = JsonSchemaObject.model_validate(ref_model) # pragma: no cover
445-
else:
446-
schema = content.schema_
445+
match content.schema_:
446+
case ReferenceObject() as schema_ref:
447+
data_type = self.get_ref_data_type(schema_ref.ref)
448+
ref_model = self.get_ref_model(schema_ref.ref)
449+
schema = JsonSchemaObject.model_validate(
450+
ref_model
451+
) # pragma: no cover
452+
case JsonSchemaObject() as schema_obj:
453+
schema = schema_obj
454+
case _:
455+
continue
447456
break
448457
if not data_type:
449458
if not schema:

fastapi_code_generator/prompt_data.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,28 @@
143143
'type': 'boolean',
144144
'choices': [],
145145
},
146+
{
147+
'name': 'allow_remote_refs',
148+
'cli_flags': ['--allow-remote-refs', '--no-allow-remote-refs'],
149+
'description': 'Allow or block fetching remote `$ref` targets '
150+
'over HTTP/HTTPS.',
151+
'required': False,
152+
'default': None,
153+
'multiple': False,
154+
'type': 'boolean | null',
155+
'choices': [],
156+
},
157+
{
158+
'name': 'allow_private_network',
159+
'cli_flags': ['--allow-private-network'],
160+
'description': 'Allow trusted remote `$ref` targets on local or '
161+
'private network addresses.',
162+
'required': False,
163+
'default': False,
164+
'multiple': False,
165+
'type': 'boolean',
166+
'choices': [],
167+
},
146168
{
147169
'name': 'output_model_type',
148170
'cli_flags': ['--output-model-type', '-d'],
@@ -255,6 +277,32 @@
255277
],
256278
'input_schema': 'openapi/custom_template_security/custom_security.yaml',
257279
},
280+
{
281+
'options': ['--allow-private-network'],
282+
'description': 'Allow trusted remote `$ref` targets on local or '
283+
'private network addresses.',
284+
'cli_args': [
285+
'--input',
286+
'openapi/remote_ref/body_and_parameters.yaml',
287+
'--output',
288+
'app',
289+
'--allow-private-network',
290+
],
291+
'input_schema': 'openapi/remote_ref/body_and_parameters.yaml',
292+
},
293+
{
294+
'options': ['--allow-remote-refs', '--no-allow-remote-refs'],
295+
'description': 'Allow or block fetching remote `$ref` targets over '
296+
'HTTP/HTTPS.',
297+
'cli_args': [
298+
'--input',
299+
'openapi/remote_ref/body_and_parameters.yaml',
300+
'--output',
301+
'app',
302+
'--allow-remote-refs',
303+
],
304+
'input_schema': 'openapi/remote_ref/body_and_parameters.yaml',
305+
},
258306
{
259307
'options': ['--include-request-argument'],
260308
'description': 'Auto-inject a FastAPI Request argument in generated '

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ classifiers = [
2828
dynamic = ["version"]
2929
dependencies = [
3030
"click>=8.1.8,<8.2",
31-
"datamodel-code-generator[http]>=0.59,<0.60",
31+
"datamodel-code-generator[http]>=0.61,<0.66",
3232
"jinja2>=2.11.2,<4",
3333
"pydantic>=2.12,<3",
3434
"pysnooper>=0.4.1,<2",

0 commit comments

Comments
 (0)