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
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: "3.14"
- run: SKIP=readme,config-types uvx prek run --all-files --show-diff-on-failure
- run: SKIP=readme,config-types,schema-docs uvx prek run --all-files --show-diff-on-failure
- if: |
github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name == github.repository ||
Expand Down
76 changes: 76 additions & 0 deletions .github/workflows/schema-docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Update Schema Docs

on:
push:
branches: [main]
paths:
- 'src/datamodel_code_generator/parser/schema_version.py'
- 'scripts/build_schema_docs.py'
- 'docs/supported_formats.md'
pull_request:
branches: [main]
paths:
- 'src/datamodel_code_generator/parser/schema_version.py'
- 'scripts/build_schema_docs.py'
- 'docs/supported_formats.md'
pull_request_target:
types: [labeled]
paths:
- 'src/datamodel_code_generator/parser/schema_version.py'
- 'scripts/build_schema_docs.py'
- 'docs/supported_formats.md'

permissions:
contents: write

jobs:
update-schema-docs:
if: |
github.event_name == 'push' ||
!github.event.pull_request.head.repo.fork ||
github.actor == 'koxudaxi' ||
github.actor == 'gaborbernat' ||
github.actor == 'ilovelinux' ||
(github.event_name == 'pull_request_target' && github.event.label.name == 'safe-to-fix' &&
(github.event.sender.login == 'koxudaxi' ||
github.event.sender.login == 'gaborbernat' ||
github.event.sender.login == 'ilovelinux'))
runs-on: ubuntu-latest
steps:
# Checkout for forks (no PAT available)
- uses: actions/checkout@v4
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
# Checkout for same-repo PRs, pushes, and pull_request_target
- uses: actions/checkout@v4
if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref || github.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
token: ${{ secrets.PAT }}
- name: Install the latest version of uv
uses: astral-sh/setup-uv@v5
- name: Install tox
run: uv tool install --python-preference only-managed --python 3.13 tox --with tox-uv
- name: Setup environment
run: tox run -vv --notest --skip-missing-interpreters false -e schema-docs
env:
UV_PYTHON_PREFERENCE: "only-managed"
- name: Build schema docs
run: .tox/schema-docs/bin/python scripts/build_schema_docs.py
- name: Commit and push if changed
if: github.event_name == 'push' || github.event_name == 'pull_request_target' || github.event.pull_request.head.repo.full_name == github.repository
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add docs/supported_formats.md
if ! git diff --staged --quiet; then
git commit -m "docs: update schema feature documentation

🤖 Generated by GitHub Actions"
git push
fi
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ repos:
language: system
files: ^src/datamodel_code_generator/config\.py$
pass_filenames: false
- id: schema-docs
name: Update schema feature documentation
entry: bash -c '[ -x .tox/schema-docs/bin/python ] && PYTHON=.tox/schema-docs/bin/python || PYTHON=.tox/schema-docs/Scripts/python; test -x "$PYTHON" || tox run -e schema-docs --notest -qq; "$PYTHON" scripts/build_schema_docs.py'
language: system
files: ^(src/datamodel_code_generator/parser/schema_version\.py|scripts/build_schema_docs\.py|docs/supported_formats\.md)$
pass_filenames: false
26 changes: 26 additions & 0 deletions docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24144,6 +24144,32 @@ datamodel-code-generator detects the OpenAPI version from the `openapi` field:
- `openapi: "3.1.x"` -> OpenAPI 3.1
- No `openapi` field -> Fallback to OpenAPI 3.1

<!-- BEGIN AUTO-GENERATED SUPPORTED FEATURES -->
### Supported Features (from code)

The following features are tracked in the codebase with their implementation status:

#### JSON Schema Features

| Feature | Introduced | Status | Description |
|---------|------------|--------|-------------|
| `Null in type array` | 2020-12 | ✅ Supported | Allows `type: ['string', 'null']` syntax for nullable types |
| `$defs` | 2019-09 | ✅ Supported | Uses `$defs` instead of `definitions` for schema definitions |
| `prefixItems` | 2020-12 | ✅ Supported | Tuple validation using `prefixItems` keyword |
| `Boolean schemas` | Draft 6 | ✅ Supported | Allows `true` and `false` as valid schemas |
| `$id` | Draft 6 | ✅ Supported | Schema identifier field (`id` in Draft 4, `$id` in Draft 6+) |
| `definitions/$defs` | Draft 4 | ✅ Supported | Key for reusable schema definitions |
| `exclusiveMinimum/Maximum as number` | Draft 6 | ✅ Supported | Numeric `exclusiveMinimum`/`exclusiveMaximum` (boolean in Draft 4) |
| `readOnly/writeOnly` | Draft 7 | ✅ Supported | Field visibility hints for read-only and write-only properties |

#### OpenAPI-Specific Features

| Feature | Introduced | Status | Description |
|---------|------------|--------|-------------|
| `nullable` | OAS 3.0 | ✅ Supported | Uses `nullable: true` for nullable types (deprecated in 3.1) |
| `discriminator` | OAS 3.0 | ✅ Supported | Polymorphism support via `discriminator` keyword |
<!-- END AUTO-GENERATED SUPPORTED FEATURES -->

## Data Format Support

### Common Formats (JSON Schema + OpenAPI)
Expand Down
26 changes: 26 additions & 0 deletions docs/supported_formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,32 @@ datamodel-code-generator detects the OpenAPI version from the `openapi` field:
- `openapi: "3.1.x"` -> OpenAPI 3.1
- No `openapi` field -> Fallback to OpenAPI 3.1

<!-- BEGIN AUTO-GENERATED SUPPORTED FEATURES -->
### Supported Features (from code)

The following features are tracked in the codebase with their implementation status:

#### JSON Schema Features

| Feature | Introduced | Status | Description |
|---------|------------|--------|-------------|
| `Null in type array` | 2020-12 | ✅ Supported | Allows `type: ['string', 'null']` syntax for nullable types |
| `$defs` | 2019-09 | ✅ Supported | Uses `$defs` instead of `definitions` for schema definitions |
| `prefixItems` | 2020-12 | ✅ Supported | Tuple validation using `prefixItems` keyword |
| `Boolean schemas` | Draft 6 | ✅ Supported | Allows `true` and `false` as valid schemas |
| `$id` | Draft 6 | ✅ Supported | Schema identifier field (`id` in Draft 4, `$id` in Draft 6+) |
| `definitions/$defs` | Draft 4 | ✅ Supported | Key for reusable schema definitions |
| `exclusiveMinimum/Maximum as number` | Draft 6 | ✅ Supported | Numeric `exclusiveMinimum`/`exclusiveMaximum` (boolean in Draft 4) |
| `readOnly/writeOnly` | Draft 7 | ✅ Supported | Field visibility hints for read-only and write-only properties |

#### OpenAPI-Specific Features

| Feature | Introduced | Status | Description |
|---------|------------|--------|-------------|
| `nullable` | OAS 3.0 | ✅ Supported | Uses `nullable: true` for nullable types (deprecated in 3.1) |
| `discriminator` | OAS 3.0 | ✅ Supported | Polymorphism support via `discriminator` keyword |
<!-- END AUTO-GENERATED SUPPORTED FEATURES -->

## Data Format Support

### Common Formats (JSON Schema + OpenAPI)
Expand Down
85 changes: 66 additions & 19 deletions scripts/build_schema_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from __future__ import annotations

import argparse
import re
import sys
from dataclasses import fields
from pathlib import Path
Expand All @@ -26,6 +27,10 @@

DOCS_PATH = Path(__file__).parent.parent / "docs" / "supported_formats.md"

# Markers for auto-generated content
BEGIN_MARKER = "<!-- BEGIN AUTO-GENERATED SUPPORTED FEATURES -->"
END_MARKER = "<!-- END AUTO-GENERATED SUPPORTED FEATURES -->"

# Status emoji mapping
STATUS_EMOJI = {
"supported": "✅",
Expand Down Expand Up @@ -114,10 +119,9 @@ def print_features_summary() -> None:
print()


def generate_supported_features_table() -> str:
"""Generate the supported features table for documentation."""
def generate_supported_features_content() -> str:
"""Generate the supported features content for documentation (without markers)."""
lines = [
"<!-- BEGIN AUTO-GENERATED SUPPORTED FEATURES -->",
"",
"### Supported Features (from code)",
"",
Expand All @@ -136,11 +140,68 @@ def generate_supported_features_table() -> str:
openapi_features = [
(name, meta) for name, meta in get_feature_metadata(OpenAPISchemaFeatures) if name not in json_field_names
]
lines.extend((generate_feature_table(openapi_features), "", "<!-- END AUTO-GENERATED SUPPORTED FEATURES -->"))
lines.extend((generate_feature_table(openapi_features), ""))

return "\n".join(lines)


def generate_supported_features_table() -> str:
"""Generate the supported features table with markers for documentation."""
return f"{BEGIN_MARKER}{generate_supported_features_content()}{END_MARKER}"


def update_docs_file(*, check: bool = False) -> int:
"""Update docs/supported_formats.md with auto-generated content.

Args:
check: If True, only check if content would change (returns 1 if different).

Returns:
0 on success, 1 on failure or if check mode detects changes.
"""
if not DOCS_PATH.exists():
print(f"Error: {DOCS_PATH} does not exist", file=sys.stderr)
return 1

current_content = DOCS_PATH.read_text(encoding="utf-8")
new_generated = generate_supported_features_content()

# Pattern to match content between markers (including markers)
pattern = re.compile(
rf"{re.escape(BEGIN_MARKER)}.*?{re.escape(END_MARKER)}",
re.DOTALL,
)

if pattern.search(current_content):
# Replace existing auto-generated section
new_content = pattern.sub(
f"{BEGIN_MARKER}{new_generated}{END_MARKER}",
current_content,
)
else:
print(f"Warning: Markers not found in {DOCS_PATH}", file=sys.stderr)
print("Please add the following markers to the file:", file=sys.stderr)
print(f" {BEGIN_MARKER}", file=sys.stderr)
print(f" {END_MARKER}", file=sys.stderr)
return 1

if check:
if current_content != new_content:
print(f"Schema docs are out of date: {DOCS_PATH}", file=sys.stderr)
print("Run 'python scripts/build_schema_docs.py' to update.", file=sys.stderr)
return 1
print("Schema docs are up to date.")
return 0

if current_content == new_content:
print("Schema docs are already up to date.")
return 0

DOCS_PATH.write_text(new_content, encoding="utf-8")
print(f"Updated {DOCS_PATH}")
return 0


def main() -> int:
"""Parse arguments and build documentation."""
parser = argparse.ArgumentParser(description="Build schema documentation from code metadata")
Expand All @@ -160,21 +221,7 @@ def main() -> int:
print_features_summary()
return 0

print("Schema Documentation Builder")
print("-" * 40)

# For now, just print the generated table
print("\nGenerated Supported Features Table:")
print()
print(generate_supported_features_table())

if args.check:
print("\n[Check mode] No files modified.")
else:
print("\n[Info] This script currently outputs to stdout.")
print(" Future versions will update docs/supported_formats.md directly.")

return 0
return update_docs_file(check=args.check)


if __name__ == "__main__":
Expand Down
7 changes: 7 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ commands =
check-wheel-contents --no-config {env_tmp_dir}
dependency_groups = pkg-meta

[testenv:schema-docs]
description = Build schema feature documentation (use --check to validate only)
commands =
python scripts/build_schema_docs.py {posargs}
dependency_groups =
no_default_groups = true

[testenv:llms-txt]
description = Generate llms.txt files (use --check to validate only, requires Python 3.11+)
commands =
Expand Down
Loading