Skip to content

Commit 32345b2

Browse files
committed
Merge branch 'main' into required-nullable-annotated
2 parents b760b64 + 2df42fb commit 32345b2

303 files changed

Lines changed: 9806 additions & 1998 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yaml

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@ jobs:
2525
os: [ubuntu-24.04, windows-latest, macos-latest]
2626
tox_env: ['']
2727
include:
28-
- tox_env: py3.12-black24-parallel
29-
name: py3.12-black24
30-
- tox_env: py3.12-black23-parallel
31-
name: py3.12-black23
32-
- tox_env: py3.12-black22-parallel
33-
name: py3.12-black22
34-
- tox_env: py3.12-isort7-parallel
35-
name: py3.12-isort7
36-
- tox_env: py3.12-isort6-parallel
37-
name: py3.12-isort6
38-
- tox_env: py3.12-isort5-parallel
39-
name: py3.12-isort5
40-
- tox_env: py3.12-pydantic1-parallel
41-
name: py3.12-pydantic1
28+
- tox_env: py312-black24-parallel
29+
name: py312-black24
30+
- tox_env: py312-black23-parallel
31+
name: py312-black23
32+
- tox_env: py312-black22-parallel
33+
name: py312-black22
34+
- tox_env: py312-isort7-parallel
35+
name: py312-isort7
36+
- tox_env: py312-isort6-parallel
37+
name: py312-isort6
38+
- tox_env: py312-isort5-parallel
39+
name: py312-isort5
40+
- tox_env: py312-pydantic1-parallel
41+
name: py312-pydantic1
4242
runs-on: ${{ matrix.os == '' && 'ubuntu-24.04' || matrix.os }}
4343
env:
4444
OS: ${{ matrix.os == '' && 'ubuntu-24.04' || matrix.os}}
@@ -54,18 +54,39 @@ jobs:
5454
- name: Install tox
5555
run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv
5656
- name: Setup Python test environment
57-
run: tox run -vv --notest --skip-missing-interpreters false -e ${{ matrix.py && format('{0}-parallel', matrix.py) || matrix.tox_env }}
57+
run: |
58+
if [ -n "${{ matrix.py }}" ]; then
59+
TOX_ENV="py${{ matrix.py }}-parallel"
60+
TOX_ENV="${TOX_ENV//.}"
61+
else
62+
TOX_ENV="${{ matrix.tox_env }}"
63+
fi
64+
tox run -vv --notest --skip-missing-interpreters false -e $TOX_ENV
65+
shell: bash
5866
env:
5967
UV_PYTHON_PREFERENCE: "only-managed"
6068
- name: Run test suite
61-
run: tox run --skip-uv-sync --skip-pkg-install -e ${{ matrix.py && format('{0}-parallel', matrix.py) || matrix.tox_env }}
69+
run: |
70+
if [ -n "${{ matrix.py }}" ]; then
71+
TOX_ENV="py${{ matrix.py }}-parallel"
72+
TOX_ENV="${TOX_ENV//.}"
73+
else
74+
TOX_ENV="${{ matrix.tox_env }}"
75+
fi
76+
tox run --skip-uv-sync --skip-pkg-install -e $TOX_ENV
77+
shell: bash
6278
env:
6379
UV_PYTHON_PREFERENCE: "only-managed"
6480
- name: Rename coverage report file
6581
run: |
6682
import os; import sys
67-
env_name = "${{ matrix.py && format('{0}-parallel', matrix.py) || matrix.tox_env }}"
68-
base_name = "${{ matrix.py || matrix.tox_env }}"
83+
py = "${{ matrix.py }}"
84+
if py:
85+
env_name = f"py{py.replace('.', '')}-parallel"
86+
base_name = py
87+
else:
88+
env_name = "${{ matrix.tox_env }}"
89+
base_name = "${{ matrix.tox_env }}"
6990
os.rename(f".tox/.coverage.{env_name}", f".tox/.coverage.{base_name}-${{ matrix.os }}")
7091
shell: python
7192
- name: Upload coverage data

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ repos:
1818
- id: pyproject-fmt
1919
args: ["--max-supported-python=3.14"]
2020
- repo: https://github.com/astral-sh/ruff-pre-commit
21-
rev: 'v0.14.7'
21+
rev: 'v0.14.8'
2222
hooks:
2323
- id: ruff-format
2424
exclude: "^tests/data"

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,12 @@ Typing customization:
393393
corresponding con* constrained types.
394394
--use-one-literal-as-default
395395
Use one literal as default value for one literal field
396+
--use-serialize-as-any
397+
Use pydantic.SerializeAsAny for fields with types that have subtypes
398+
(Pydantic v2 only)
396399
--use-specialized-enum, --no-use-specialized-enum
397-
Don''t use specialized Enum class (StrEnum, IntEnum) even if the
398-
target Python version supports it
400+
Use specialized Enum class (StrEnum, IntEnum). Requires --target-
401+
python-version 3.11+
399402
--use-standard-collections
400403
Use standard collections for type hinting (list, dict)
401404
--use-subclass-enum Define generic Enum class as subclass with field type when enum has
@@ -441,6 +444,8 @@ Field customization:
441444
default values.
442445
--use-field-description
443446
Use schema description to populate field docstring
447+
--use-frozen-field Use Field(frozen=True) for readOnly fields (Pydantic v2) or
448+
Field(allow_mutation=False) (Pydantic v1)
444449
--use-inline-field-description
445450
Use schema description to populate field docstring as inline
446451
docstring
@@ -566,7 +571,10 @@ General options:
566571
--generate-pyproject-config
567572
Generate pyproject.toml configuration from the provided CLI
568573
arguments and exit
574+
--ignore-pyproject Ignore pyproject.toml configuration
569575
--no-color disable colorized output
576+
--profile PROFILE Use a named profile from pyproject.toml [tool.datamodel-
577+
codegen.profiles.<name>]
570578
--version show version
571579
-h, --help show this help message and exit
572580
```

docs/index.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,12 @@ Typing customization:
385385
corresponding con* constrained types.
386386
--use-one-literal-as-default
387387
Use one literal as default value for one literal field
388+
--use-serialize-as-any
389+
Use pydantic.SerializeAsAny for fields with types that have subtypes
390+
(Pydantic v2 only)
388391
--use-specialized-enum, --no-use-specialized-enum
389-
Don''t use specialized Enum class (StrEnum, IntEnum) even if the
390-
target Python version supports it
392+
Use specialized Enum class (StrEnum, IntEnum). Requires --target-
393+
python-version 3.11+
391394
--use-standard-collections
392395
Use standard collections for type hinting (list, dict)
393396
--use-subclass-enum Define generic Enum class as subclass with field type when enum has
@@ -433,6 +436,8 @@ Field customization:
433436
default values.
434437
--use-field-description
435438
Use schema description to populate field docstring
439+
--use-frozen-field Use Field(frozen=True) for readOnly fields (Pydantic v2) or
440+
Field(allow_mutation=False) (Pydantic v1)
436441
--use-inline-field-description
437442
Use schema description to populate field docstring as inline
438443
docstring
@@ -558,7 +563,10 @@ General options:
558563
--generate-pyproject-config
559564
Generate pyproject.toml configuration from the provided CLI
560565
arguments and exit
566+
--ignore-pyproject Ignore pyproject.toml configuration
561567
--no-color disable colorized output
568+
--profile PROFILE Use a named profile from pyproject.toml [tool.datamodel-
569+
codegen.profiles.<name>]
562570
--version show version
563571
-h, --help show this help message and exit
564572
```

docs/pyproject_toml.md

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,115 @@
1-
datamodel-code-generator has a lot of command-line options.
1+
# pyproject.toml Configuration
22

3-
The options are supported on `pyproject.toml`.
3+
datamodel-code-generator can be configured using `pyproject.toml`. The tool automatically searches for `pyproject.toml` in the current directory and parent directories (stopping at the git repository root).
44

5-
Example `pyproject.toml`:
6-
```toml
5+
## Basic Usage
6+
7+
```toml
78
[tool.datamodel-codegen]
9+
input = "schema.yaml"
10+
output = "models.py"
11+
target-python-version = "3.11"
12+
snake-case-field = true
813
field-constraints = true
14+
```
15+
16+
All CLI options can be used in `pyproject.toml` by converting them to kebab-case (e.g., `--snake-case-field` becomes `snake-case-field`).
17+
18+
## Named Profiles
19+
20+
You can define multiple named profiles for different use cases within a single project:
21+
22+
```toml
23+
[tool.datamodel-codegen]
24+
target-python-version = "3.9"
25+
snake-case-field = true
26+
27+
[tool.datamodel-codegen.profiles.api]
28+
input = "schemas/api.yaml"
29+
output = "src/models/api.py"
30+
target-python-version = "3.11"
31+
32+
[tool.datamodel-codegen.profiles.database]
33+
input = "schemas/db.json"
34+
output = "src/models/db.py"
35+
input-file-type = "jsonschema"
36+
```
37+
38+
Base settings in `[tool.datamodel-codegen]` are used when no profile is specified, and also serve as defaults for profiles.
39+
40+
Use a profile with the `--profile` option:
41+
42+
```bash
43+
datamodel-codegen --profile api
44+
datamodel-codegen --profile database
45+
```
46+
47+
## Configuration Priority
48+
49+
Settings are applied in the following priority order (highest to lowest):
50+
51+
1. **CLI arguments** - Always take precedence
52+
2. **Profile settings** - From `[tool.datamodel-codegen.profiles.<name>]`
53+
3. **Base settings** - From `[tool.datamodel-codegen]`
54+
4. **Default values** - Built-in defaults
55+
56+
## Merge Rules
57+
58+
When using profiles, settings are merged using **shallow merge**:
59+
60+
- Profile values **completely replace** base values (no deep merging)
61+
- Settings not specified in the profile are inherited from the base configuration
62+
- Lists and dictionaries are replaced entirely, not merged
63+
64+
### Example
65+
66+
```toml
67+
[tool.datamodel-codegen]
68+
strict-types = ["str", "int"]
69+
http-headers = ["Authorization: Bearer token"]
70+
71+
[tool.datamodel-codegen.profiles.api]
72+
strict-types = ["bytes"]
73+
```
74+
75+
When using `--profile api`:
76+
77+
- `strict-types` becomes `["bytes"]` (completely replaces base, not merged)
78+
- `http-headers` is inherited from base as `["Authorization: Bearer token"]`
79+
80+
## Ignoring pyproject.toml
81+
82+
To ignore all `pyproject.toml` configuration and use only CLI arguments:
83+
84+
```bash
85+
datamodel-codegen --ignore-pyproject --input schema.yaml --output models.py
86+
```
87+
88+
## Generating Configuration
89+
90+
Generate a `pyproject.toml` configuration section from CLI arguments:
91+
92+
```bash
93+
datamodel-codegen --input schema.yaml --output models.py --snake-case-field --generate-pyproject-config
94+
```
95+
96+
Output:
97+
98+
```toml
99+
[tool.datamodel-codegen]
100+
input = "schema.yaml"
101+
output = "models.py"
9102
snake-case-field = true
10-
strip-default-none = false
11-
target-python-version = "3.7"
103+
```
104+
105+
Generate CLI command from existing `pyproject.toml`:
106+
107+
```bash
108+
datamodel-codegen --generate-cli-command
109+
```
110+
111+
With a specific profile:
112+
113+
```bash
114+
datamodel-codegen --profile api --generate-cli-command
12115
```

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ filterwarnings = [
208208
"ignore:^.*black doesn't support `experimental-string-processing` option for wrapping string literal in .*",
209209
"ignore:^.*jsonschema.exceptions.RefResolutionError is deprecated as of version 4.18.0. If you wish to catch potential reference resolution errors, directly catch referencing.exceptions.Unresolvable..*",
210210
"ignore:^.*`experimental string processing` has been included in `preview` and deprecated. Use `preview` instead..*",
211+
"ignore:^.*No schemas found in components/schemas.*",
211212
]
212213
norecursedirs = "tests/data/*"
213214
verbosity_assertions = 2

src/datamodel_code_generator/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
418418
http_headers: Sequence[tuple[str, str]] | None = None,
419419
http_ignore_tls: bool = False,
420420
use_annotated: bool = False,
421+
use_serialize_as_any: bool = False,
421422
use_non_positive_negative_number_constrained_types: bool = False,
422423
original_field_name_delimiter: str | None = None,
423424
use_double_quotes: bool = False,
@@ -442,7 +443,9 @@ def generate( # noqa: PLR0912, PLR0913, PLR0914, PLR0915
442443
keyword_only: bool = False,
443444
frozen_dataclasses: bool = False,
444445
no_alias: bool = False,
446+
use_frozen_field: bool = False,
445447
formatters: list[Formatter] = DEFAULT_FORMATTERS,
448+
settings_path: Path | None = None,
446449
parent_scoped_naming: bool = False,
447450
dataclass_arguments: DataclassArguments | None = None,
448451
disable_future_imports: bool = False,
@@ -656,6 +659,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
656659
http_headers=http_headers,
657660
http_ignore_tls=http_ignore_tls,
658661
use_annotated=use_annotated,
662+
use_serialize_as_any=use_serialize_as_any,
659663
use_non_positive_negative_number_constrained_types=use_non_positive_negative_number_constrained_types,
660664
original_field_name_delimiter=original_field_name_delimiter,
661665
use_double_quotes=use_double_quotes,
@@ -679,6 +683,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
679683
keyword_only=keyword_only,
680684
frozen_dataclasses=frozen_dataclasses,
681685
no_alias=no_alias,
686+
use_frozen_field=use_frozen_field,
682687
formatters=formatters,
683688
encoding=encoding,
684689
parent_scoped_naming=parent_scoped_naming,
@@ -690,6 +695,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
690695

691696
with chdir(output):
692697
results = parser.parse(
698+
settings_path=settings_path,
693699
disable_future_imports=disable_future_imports,
694700
all_exports_scope=all_exports_scope,
695701
all_exports_collision_strategy=all_exports_collision_strategy,
@@ -775,7 +781,7 @@ def get_header_and_first_line(csv_file: IO[str]) -> dict[str, Any]:
775781
if header_after:
776782
content = header_before + "\n" + extracted_future + "\n\n" + header_after
777783
else:
778-
content = header_before + "\n\n\n" + extracted_future
784+
content = header_before + "\n\n" + extracted_future
779785
print(content, file=file)
780786
print(file=file)
781787
print(body_without_future.rstrip(), file=file)

0 commit comments

Comments
 (0)