Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions docs/cli-reference/general-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
| [`--generate-pyproject-config`](#generate-pyproject-config) | Generate pyproject.toml configuration from CLI arguments. |
| [`--http-headers`](#http-headers) | Fetch schema from URL with custom HTTP headers. |
| [`--http-ignore-tls`](#http-ignore-tls) | Disable TLS certificate verification for HTTPS requests. |
| [`--http-local-ref-path`](#http-local-ref-path) | Resolve HTTP references from local schema files. |
| [`--http-query-parameters`](#http-query-parameters) | Add query parameters to HTTP requests for remote schemas. |
| [`--http-timeout`](#http-timeout) | Set timeout for HTTP requests to remote hosts. |
| [`--ignore-pyproject`](#ignore-pyproject) | Ignore pyproject.toml configuration file. |
Expand Down Expand Up @@ -1631,6 +1632,59 @@ environments with self-signed certificates. Not recommended for production.

---

## `--http-local-ref-path` {#http-local-ref-path}

Resolve HTTP references from local schema files.

The `--http-local-ref-path` flag maps HTTP(S) `$ref` URLs to files under
a local schema store instead of fetching them from the network. The host and
URL path are used as the relative path under the schema store. For example,
`https://api.example.com/schemas/pet.json` is read from
`schemas/api.example.com/schemas/pet.json`.

!!! tip "Usage"

```bash
datamodel-codegen --input schema.json --url https://api.example.com/schema.json --http-local-ref-path schemas # (1)!
```

1. :material-arrow-left: `--http-local-ref-path` - the option documented here

??? example "Examples"

**Input Schema:**

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "https://api.example.com/schemas/pet.json"
}
```

**Output:**

```python
# generated by datamodel-codegen:
# filename: https://api.example.com/schema.json
# timestamp: 2019-07-26T00:00:00+00:00

from __future__ import annotations

from pydantic import BaseModel, RootModel


class Pet(BaseModel):
id: int | None = None
name: str | None = None
tag: str | None = None


class Model(RootModel[Pet]):
root: Pet
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

---

## `--http-query-parameters` {#http-query-parameters}

Add query parameters to HTTP requests for remote schemas.
Expand Down
3 changes: 2 additions & 1 deletion docs/cli-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This documentation is auto-generated from test cases.
| 🎨 [Template Customization](template-customization.md) | 21 | Output formatting and custom rendering |
| 📘 [OpenAPI-only Options](openapi-only-options.md) | 7 | OpenAPI-specific features |
| 📋 [GraphQL-only Options](graphql-only-options.md) | 1 | |
| ⚙️ [General Options](general-options.md) | 16 | Utilities and meta options |
| ⚙️ [General Options](general-options.md) | 17 | Utilities and meta options |
| 📝 [Utility Options](utility-options.md) | 6 | Help, version, debug options |

## All Options
Expand Down Expand Up @@ -105,6 +105,7 @@ This documentation is auto-generated from test cases.
- [`--help`](utility-options.md#help)
- [`--http-headers`](general-options.md#http-headers)
- [`--http-ignore-tls`](general-options.md#http-ignore-tls)
- [`--http-local-ref-path`](general-options.md#http-local-ref-path)
- [`--http-query-parameters`](general-options.md#http-query-parameters)
- [`--http-timeout`](general-options.md#http-timeout)

Expand Down
19 changes: 4 additions & 15 deletions docs/cli-reference/model-customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -5272,6 +5272,10 @@ Use default values from schema in generated models.
The `--use-default` flag allows required fields with default values to be generated
with their defaults, making them optional to provide when instantiating the model.

When `--strict-nullable` is enabled, the field type still follows the schema's
nullability. For example, a required string field with a default is generated
as `str = 'value'`, not `str | None = 'value'`, unless the schema allows null.

**Related:** [`--strict-nullable`](model-customization.md#strict-nullable)

!!! tip "Usage"
Expand All @@ -5282,21 +5286,6 @@ with their defaults, making them optional to provide when instantiating the mode

1. :material-arrow-left: `--use-default` - the option documented here

!!! warning "Fields with defaults become nullable"
When using `--use-default`, fields with default values are generated as nullable
types (e.g., `str | None` instead of `str`), even if the schema does not allow
null values.

If you want fields to strictly follow the schema's type definition (non-nullable),
use `--strict-nullable` together with `--use-default`.


!!! note "Future behavior change"
In a future major version, the default behavior of `--use-default` may change to
generate non-nullable types that match the schema definition (equivalent to using
`--strict-nullable`). If you rely on the current nullable behavior, consider
explicitly handling this in your code.

??? example "Examples"

**Input Schema:**
Expand Down
2 changes: 2 additions & 0 deletions docs/cli-reference/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ datamodel-codegen [OPTIONS]
| [`--generate-pyproject-config`](general-options.md#generate-pyproject-config) | Generate pyproject.toml configuration from CLI arguments. |
| [`--http-headers`](general-options.md#http-headers) | Fetch schema from URL with custom HTTP headers. |
| [`--http-ignore-tls`](general-options.md#http-ignore-tls) | Disable TLS certificate verification for HTTPS requests. |
| [`--http-local-ref-path`](general-options.md#http-local-ref-path) | Resolve HTTP references from local schema files. |
| [`--http-query-parameters`](general-options.md#http-query-parameters) | Add query parameters to HTTP requests for remote schemas. |
| [`--http-timeout`](general-options.md#http-timeout) | Set timeout for HTTP requests to remote hosts. |
| [`--ignore-pyproject`](general-options.md#ignore-pyproject) | Ignore pyproject.toml configuration file. |
Expand Down Expand Up @@ -275,6 +276,7 @@ All options sorted alphabetically:
- [`--help`](utility-options.md#help) - Show help message and exit
- [`--http-headers`](general-options.md#http-headers) - Fetch schema from URL with custom HTTP headers.
- [`--http-ignore-tls`](general-options.md#http-ignore-tls) - Disable TLS certificate verification for HTTPS requests.
- [`--http-local-ref-path`](general-options.md#http-local-ref-path) - Resolve HTTP references from local schema files.
- [`--http-query-parameters`](general-options.md#http-query-parameters) - Add query parameters to HTTP requests for remote schemas.
- [`--http-timeout`](general-options.md#http-timeout) - Set timeout for HTTP requests to remote hosts.
- [`--ignore-enum-constraints`](typing-customization.md#ignore-enum-constraints) - Ignore enum constraints and use base string type instead of ...
Expand Down
3 changes: 3 additions & 0 deletions src/datamodel_code_generator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ def validate_file(cls, value: Any) -> TextIOBase | None: # noqa: N805
"output",
"custom_template_dir",
"custom_file_header_path",
"http_local_ref_path",
mode="before",
)
def validate_path(cls, value: Any) -> Path | None: # noqa: N805
Expand Down Expand Up @@ -493,6 +494,7 @@ def validate_class_name_affix_scope(cls, v: str | ClassNameAffixScope | None) ->
allof_class_hierarchy: AllOfClassHierarchy = AllOfClassHierarchy.IfNoConflict
allow_remote_refs: Optional[bool] = None # noqa: UP045
http_headers: Optional[Sequence[tuple[str, str]]] = None # noqa: UP045
http_local_ref_path: Optional[Path] = None # noqa: UP045
http_ignore_tls: bool = False
http_timeout: Optional[float] = None # noqa: UP045
use_annotated: bool = False
Expand Down Expand Up @@ -944,6 +946,7 @@ def run_generate_from_config( # noqa: PLR0913, PLR0917
allof_class_hierarchy=config.allof_class_hierarchy,
allow_remote_refs=config.allow_remote_refs,
http_headers=config.http_headers,
http_local_ref_path=config.http_local_ref_path,
http_ignore_tls=config.http_ignore_tls,
http_timeout=config.http_timeout,
use_annotated=config.use_annotated,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class GenerateConfigDict(TypedDict, closed=True):
allof_class_hierarchy: NotRequired[AllOfClassHierarchy]
allow_remote_refs: NotRequired[bool | None]
http_headers: NotRequired[Sequence[tuple[str, str]] | None]
http_local_ref_path: NotRequired[Path | None]
http_ignore_tls: NotRequired[bool]
http_timeout: NotRequired[float | None]
use_annotated: NotRequired[bool]
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/_types/parser_config_dicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class ParserConfigDict(TypedDict):
allof_class_hierarchy: NotRequired[AllOfClassHierarchy]
allow_remote_refs: NotRequired[bool | None]
http_headers: NotRequired[Sequence[tuple[str, str]] | None]
http_local_ref_path: NotRequired[Path | None]
http_ignore_tls: NotRequired[bool]
http_timeout: NotRequired[float | None]
use_annotated: NotRequired[bool]
Expand Down
7 changes: 7 additions & 0 deletions src/datamodel_code_generator/arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ def start_section(self, heading: str | None) -> None:
default=None,
help="Timeout in seconds for HTTP requests to remote hosts (default: 30)",
)
base_options.add_argument(
"--http-local-ref-path",
help=(
"Resolve HTTP(S) JSON Schema $ref URLs from a local directory instead of fetching them. "
"URLs are mapped under the directory by host and path; extensionless refs also try '.json'."
),
)
base_options.add_argument(
"--input",
help="Input file/directory (default: stdin)",
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ class CLIOptionMeta:
"--allow-remote-refs": CLIOptionMeta(name="--allow-remote-refs", category=OptionCategory.GENERAL),
"--no-allow-remote-refs": CLIOptionMeta(name="--no-allow-remote-refs", category=OptionCategory.GENERAL),
"--http-headers": CLIOptionMeta(name="--http-headers", category=OptionCategory.GENERAL),
"--http-local-ref-path": CLIOptionMeta(name="--http-local-ref-path", category=OptionCategory.GENERAL),
"--http-ignore-tls": CLIOptionMeta(name="--http-ignore-tls", category=OptionCategory.GENERAL),
"--http-query-parameters": CLIOptionMeta(name="--http-query-parameters", category=OptionCategory.GENERAL),
"--http-timeout": CLIOptionMeta(name="--http-timeout", category=OptionCategory.GENERAL),
Expand Down
2 changes: 2 additions & 0 deletions src/datamodel_code_generator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class GenerateConfig(BaseModel):
allof_class_hierarchy: AllOfClassHierarchy = AllOfClassHierarchy.IfNoConflict
allow_remote_refs: bool | None = None
http_headers: Sequence[tuple[str, str]] | None = None
http_local_ref_path: Path | None = None
http_ignore_tls: bool = False
http_timeout: float | None = None
use_annotated: bool = False
Expand Down Expand Up @@ -276,6 +277,7 @@ class ParserConfig(BaseModel):
allof_class_hierarchy: AllOfClassHierarchy = AllOfClassHierarchy.IfNoConflict
allow_remote_refs: bool | None = None
http_headers: Sequence[tuple[str, str]] | None = None
http_local_ref_path: Path | None = None
http_ignore_tls: bool = False
http_timeout: float | None = None
use_annotated: bool = False
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/parser/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ def __init__( # noqa: PLR0912, PLR0915
self.wrap_string_literal: bool | None = config.wrap_string_literal
self.allow_remote_refs: bool | None = config.allow_remote_refs
self.http_headers: Sequence[tuple[str, str]] | None = config.http_headers
self.http_local_ref_path: Path | None = config.http_local_ref_path
self.http_query_parameters: Sequence[tuple[str, str]] | None = config.http_query_parameters
self.http_ignore_tls: bool = config.http_ignore_tls
self.http_timeout: float | None = config.http_timeout
Expand Down
39 changes: 36 additions & 3 deletions src/datamodel_code_generator/parser/jsonschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from functools import cached_property, lru_cache
from pathlib import Path
from typing import TYPE_CHECKING, Any, ClassVar, Literal, Optional, Union
from urllib.parse import ParseResult, unquote
from urllib.parse import ParseResult, unquote, urlparse
from warnings import warn

from pydantic import (
Expand Down Expand Up @@ -3822,7 +3822,7 @@ def create_enum(reference_: Reference) -> DataType:
def _get_ref_body(self, resolved_ref: str) -> dict[str, YamlValue]:
"""Get the body of a reference from URL or remote file."""
if is_url(resolved_ref):
if not resolved_ref.startswith("file://"):
if not resolved_ref.startswith("file://") and self.http_local_ref_path is None:
if self.allow_remote_refs is False:
msg = (
f"Fetching remote $ref is disabled: {resolved_ref}\n"
Expand All @@ -3842,10 +3842,41 @@ def _get_ref_body(self, resolved_ref: str) -> dict[str, YamlValue]:
return self._get_ref_body_from_url(resolved_ref)
return self._get_ref_body_from_remote(resolved_ref)

def _get_ref_body_from_local_http_path(self, ref: str) -> dict[str, YamlValue]:
assert self.http_local_ref_path is not None
parsed = urlparse(ref)
if parsed.scheme not in {"http", "https"}: # pragma: no cover
msg = f"Unsupported local HTTP $ref URL: {ref}"
raise Error(msg)

parts = [unquote(part) for part in parsed.path.split("/") if part]
if not parsed.netloc or any(part in {".", ".."} or "/" in part or "\\" in part for part in parts):
msg = f"Unsupported local HTTP $ref URL path: {ref}"
raise Error(msg)

base_path = self.http_local_ref_path.resolve()
relative_path = Path(parsed.netloc, *parts)
file_paths = [(base_path / relative_path).resolve()]
if not parts or not Path(parts[-1]).suffix:
file_paths.append((base_path / relative_path.with_name(f"{relative_path.name}.json")).resolve())

if any(not file_path.is_relative_to(base_path) for file_path in file_paths):
msg = f"Unsupported local HTTP $ref URL path: {ref}"
raise Error(msg)

for file_path in file_paths:
if file_path.is_file():
return self.remote_object_cache.get_or_put(
str(file_path),
default_factory=lambda _, file_path=file_path: load_data_from_path(file_path, self.encoding),
)

msg = f"$ref local file not found for {ref}: tried {', '.join(str(path) for path in file_paths)}"
raise Error(msg)

def _get_ref_body_from_url(self, ref: str) -> dict[str, YamlValue]:
"""Get reference body from a URL (HTTP, HTTPS, or file scheme)."""
if ref.startswith("file://"):
from urllib.parse import urlparse # noqa: PLC0415
from urllib.request import url2pathname # noqa: PLC0415

parsed = urlparse(ref)
Expand All @@ -3858,6 +3889,8 @@ def _get_ref_body_from_url(self, ref: str) -> dict[str, YamlValue]:
return self.remote_object_cache.get_or_put(
ref, default_factory=lambda _: load_data_from_path(file_path, self.encoding)
)
if self.http_local_ref_path is not None and urlparse(ref).scheme in {"http", "https"}:
return self._get_ref_body_from_local_http_path(ref)
return self.remote_object_cache.get_or_put(
ref, default_factory=lambda key: load_data(self._get_text_from_url(key))
)
Expand Down
1 change: 1 addition & 0 deletions src/datamodel_code_generator/prompt_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"--graphql-no-typename": "Exclude __typename field from generated GraphQL models.",
"--http-headers": "Fetch schema from URL with custom HTTP headers.",
"--http-ignore-tls": "Disable TLS certificate verification for HTTPS requests.",
"--http-local-ref-path": "Resolve HTTP references from local schema files.",
"--http-query-parameters": "Add query parameters to HTTP requests for remote schemas.",
"--http-timeout": "Set timeout for HTTP requests to remote hosts.",
"--ignore-enum-constraints": "Ignore enum constraints and use base string type instead of Enum classes.",
Expand Down
5 changes: 5 additions & 0 deletions tests/data/expected/main/help/color.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ For detailed usage, see: https://datamodel-code-generator.koxudaxi.dev
(example: "Authorization: Basic dXNlcjpwYXNz")
--http-ignore-tls Disable verification of the remote host's TLS
certificate
--http-local-ref-path HTTP_LOCAL_REF_PATH
Resolve HTTP(S) JSON Schema $ref URLs from a local
directory instead of fetching them. URLs are mapped
under the directory by host and path; extensionless
refs also try '.json'.
--http-query-parameters HTTP_QUERY_PARAMETERS [HTTP_QUERY_PARAMETERS ...]
Set query parameters in HTTP requests to the remote
host. (example: "ref=branch")
Expand Down
5 changes: 5 additions & 0 deletions tests/data/expected/main/help/no_color.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ Options:
(example: "Authorization: Basic dXNlcjpwYXNz")
--http-ignore-tls Disable verification of the remote host's TLS
certificate
--http-local-ref-path HTTP_LOCAL_REF_PATH
Resolve HTTP(S) JSON Schema $ref URLs from a local
directory instead of fetching them. URLs are mapped
under the directory by host and path; extensionless
refs also try '.json'.
--http-query-parameters HTTP_QUERY_PARAMETERS [HTTP_QUERY_PARAMETERS ...]
Set query parameters in HTTP requests to the remote
host. (example: "ref=branch")
Expand Down
1 change: 1 addition & 0 deletions tests/data/expected/main/input_model/config_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ class GenerateConfig(TypedDict, closed=True):
allof_class_hierarchy: NotRequired[AllOfClassHierarchy]
allow_remote_refs: NotRequired[bool | None]
http_headers: NotRequired[Sequence[tuple[str, str]] | None]
http_local_ref_path: NotRequired[str | None]
http_ignore_tls: NotRequired[bool]
http_timeout: NotRequired[float | None]
use_annotated: NotRequired[bool]
Expand Down
6 changes: 6 additions & 0 deletions tests/data/expected/main_kr/generate_prompt/basic.txt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
- `--generate-pyproject-config`: Generate pyproject.toml configuration from CLI arguments.
- `--http-headers`: Fetch schema from URL with custom HTTP headers.
- `--http-ignore-tls`: Disable TLS certificate verification for HTTPS requests.
- `--http-local-ref-path`: Resolve HTTP references from local schema files.
- `--http-query-parameters`: Add query parameters to HTTP requests for remote schemas.
- `--http-timeout`: Set timeout for HTTP requests to remote hosts.
- `--ignore-pyproject`: Ignore pyproject.toml configuration file.
Expand Down Expand Up @@ -215,6 +216,11 @@ For detailed usage, see: https://datamodel-code-generator.koxudaxi.dev
(example: "Authorization: Basic dXNlcjpwYXNz")
--http-ignore-tls Disable verification of the remote host's TLS
certificate
--http-local-ref-path HTTP_LOCAL_REF_PATH
Resolve HTTP(S) JSON Schema $ref URLs from a local
directory instead of fetching them. URLs are mapped
under the directory by host and path; extensionless
refs also try '.json'.
--http-query-parameters HTTP_QUERY_PARAMETERS [HTTP_QUERY_PARAMETERS ...]
Set query parameters in HTTP requests to the remote
host. (example: "ref=branch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
- `--generate-pyproject-config`: Generate pyproject.toml configuration from CLI arguments.
- `--http-headers`: Fetch schema from URL with custom HTTP headers.
- `--http-ignore-tls`: Disable TLS certificate verification for HTTPS requests.
- `--http-local-ref-path`: Resolve HTTP references from local schema files.
- `--http-query-parameters`: Add query parameters to HTTP requests for remote schemas.
- `--http-timeout`: Set timeout for HTTP requests to remote hosts.
- `--ignore-pyproject`: Ignore pyproject.toml configuration file.
Expand Down Expand Up @@ -215,6 +216,11 @@ For detailed usage, see: https://datamodel-code-generator.koxudaxi.dev
(example: "Authorization: Basic dXNlcjpwYXNz")
--http-ignore-tls Disable verification of the remote host's TLS
certificate
--http-local-ref-path HTTP_LOCAL_REF_PATH
Resolve HTTP(S) JSON Schema $ref URLs from a local
directory instead of fetching them. URLs are mapped
under the directory by host and path; extensionless
refs also try '.json'.
--http-query-parameters HTTP_QUERY_PARAMETERS [HTTP_QUERY_PARAMETERS ...]
Set query parameters in HTTP requests to the remote
host. (example: "ref=branch")
Expand Down
Loading
Loading