Skip to content

Commit e9161eb

Browse files
committed
Address docs examples review
1 parent 65f2455 commit e9161eb

10 files changed

Lines changed: 295 additions & 71 deletions

docs/llms-full.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29480,7 +29480,7 @@ components:
2948029480
```
2948129481
<!-- END AUTO-GENERATED DOC EXAMPLE: openapi.read-only-write-only-allof.schema -->
2948229482

29483-
Generated `UserRequest` will exclude `created_at` (readOnly from Timestamps).
29483+
Generated `UserRequest` will exclude `created_at`, `updated_at`, and `id` because they are readOnly fields from the flattened Timestamps/User schemas. Generated `UserResponse` will exclude `password` and `api_key` because they are writeOnly fields from Credentials.
2948429484

2948529485
### ⚠️ Collision Handling
2948629486

@@ -31431,6 +31431,9 @@ models/
3143131431
`-- shared.py
3143231432
```
3143331433

31434+
**models/__init__.py:**
31435+
_No code beyond the generated header._
31436+
3143431437
**models/schema_a.py:**
3143531438

3143631439
```python

docs/model-reuse.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ models/
129129
`-- shared.py
130130
```
131131

132+
**models/__init__.py:**
133+
_No code beyond the generated header._
134+
132135
**models/schema_a.py:**
133136

134137
```python

docs/openapi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ components:
421421
```
422422
<!-- END AUTO-GENERATED DOC EXAMPLE: openapi.read-only-write-only-allof.schema -->
423423

424-
Generated `UserRequest` will exclude `created_at` (readOnly from Timestamps).
424+
Generated `UserRequest` will exclude `created_at`, `updated_at`, and `id` because they are readOnly fields from the flattened Timestamps/User schemas. Generated `UserResponse` will exclude `password` and `api_key` because they are writeOnly fields from Credentials.
425425

426426
### ⚠️ Collision Handling
427427

scripts/build_docs_examples.py

Lines changed: 93 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
PYTHON_GENERATED_HEADER_PATTERN = re.compile(
2424
r"\A# generated by datamodel-codegen:\n"
2525
r"# filename: .+\n"
26-
r"(?:# timestamp:\s+.+\n)?"
27-
r"\n",
26+
r"(?:# timestamp:\s+.+(?:\n|$))?"
27+
r"\n?",
2828
)
2929

3030

@@ -63,10 +63,15 @@ def render_file_example(*, language: str, path: Path, strip_python_header: bool
6363
return fenced(language, content)
6464

6565

66+
def directory_files(path: Path) -> list[Path]:
67+
"""Return stable file entries for generated directory examples."""
68+
return sorted(file for file in path.iterdir() if file.is_file())
69+
70+
6671
def directory_tree(path: Path, *, root_name: str) -> str:
6772
"""Render a stable text tree for generated directory examples."""
6873
lines = [f"{root_name}/"]
69-
files = sorted(file for file in path.iterdir() if file.is_file())
74+
files = directory_files(path)
7075
for index, file in enumerate(files):
7176
branch = "`--" if index == len(files) - 1 else "|--"
7277
lines.append(f"{branch} {file.name}")
@@ -81,14 +86,35 @@ def render_reuse_scope_tree_example() -> str:
8186
"**Output with `--reuse-scope tree`:**",
8287
fenced("text", directory_tree(output_path, root_name="models")),
8388
]
84-
for file_name in ("schema_a.py", "schema_b.py", "shared.py"):
89+
for file in directory_files(output_path):
90+
content = read_python_output(file)
8591
lines.extend((
86-
f"**models/{file_name}:**",
87-
fenced("python", read_python_output(output_path / file_name)),
92+
f"**models/{file.name}:**",
93+
fenced("python", content) if content.strip() else "_No code beyond the generated header._\n",
8894
))
8995
return "\n".join(lines)
9096

9197

98+
def _file_example(
99+
example_id: str,
100+
doc: str,
101+
*,
102+
language: str,
103+
path: Path,
104+
strip_python_header: bool = False,
105+
) -> DocsExample:
106+
"""Build a docs example from one fixture file."""
107+
return DocsExample(
108+
example_id=example_id,
109+
path=DOCS / doc,
110+
render=lambda: render_file_example(
111+
language=language,
112+
path=path,
113+
strip_python_header=strip_python_header,
114+
),
115+
)
116+
117+
92118
def docs_examples() -> tuple[DocsExample, ...]:
93119
"""Return all generated docs example sections."""
94120
return (
@@ -97,70 +123,56 @@ def docs_examples() -> tuple[DocsExample, ...]:
97123
path=DOCS / "openapi.md",
98124
render=lambda: details("api.yaml", "yaml", read_text(TEST_DATA / "openapi" / "api.yaml")),
99125
),
100-
DocsExample(
101-
example_id="openapi.quick-start.output",
102-
path=DOCS / "openapi.md",
103-
render=lambda: render_file_example(
104-
language="python",
105-
path=EXPECTED_MAIN / "openapi" / "general.py",
106-
strip_python_header=True,
107-
),
126+
_file_example(
127+
"openapi.quick-start.output",
128+
"openapi.md",
129+
language="python",
130+
path=EXPECTED_MAIN / "openapi" / "general.py",
131+
strip_python_header=True,
108132
),
109-
DocsExample(
110-
example_id="openapi.read-only-write-only.schema",
111-
path=DOCS / "openapi.md",
112-
render=lambda: render_file_example(
113-
language="yaml",
114-
path=TEST_DATA / "openapi" / "read_only_write_only.yaml",
115-
),
133+
_file_example(
134+
"openapi.read-only-write-only.schema",
135+
"openapi.md",
136+
language="yaml",
137+
path=TEST_DATA / "openapi" / "read_only_write_only.yaml",
116138
),
117-
DocsExample(
118-
example_id="openapi.read-only-write-only.output",
119-
path=DOCS / "openapi.md",
120-
render=lambda: render_file_example(
121-
language="python",
122-
path=EXPECTED_MAIN / "openapi" / "read_only_write_only_all.py",
123-
strip_python_header=True,
124-
),
139+
_file_example(
140+
"openapi.read-only-write-only.output",
141+
"openapi.md",
142+
language="python",
143+
path=EXPECTED_MAIN / "openapi" / "read_only_write_only_all.py",
144+
strip_python_header=True,
125145
),
126-
DocsExample(
127-
example_id="openapi.read-only-write-only-allof.schema",
128-
path=DOCS / "openapi.md",
129-
render=lambda: render_file_example(
130-
language="yaml",
131-
path=TEST_DATA / "openapi" / "read_only_write_only_allof.yaml",
132-
),
146+
_file_example(
147+
"openapi.read-only-write-only-allof.schema",
148+
"openapi.md",
149+
language="yaml",
150+
path=TEST_DATA / "openapi" / "read_only_write_only_allof.yaml",
133151
),
134-
DocsExample(
135-
example_id="model-reuse.reuse-model.output",
136-
path=DOCS / "model-reuse.md",
137-
render=lambda: render_file_example(
138-
language="python",
139-
path=EXPECTED_MAIN / "jsonschema" / "json_reuse_enum.py",
140-
strip_python_header=True,
141-
),
152+
_file_example(
153+
"model-reuse.reuse-model.output",
154+
"model-reuse.md",
155+
language="python",
156+
path=EXPECTED_MAIN / "jsonschema" / "json_reuse_enum.py",
157+
strip_python_header=True,
142158
),
143159
DocsExample(
144160
example_id="model-reuse.reuse-scope-tree.output",
145161
path=DOCS / "model-reuse.md",
146162
render=render_reuse_scope_tree_example,
147163
),
148-
DocsExample(
149-
example_id="model-reuse.use-type-alias.schema",
150-
path=DOCS / "model-reuse.md",
151-
render=lambda: render_file_example(
152-
language="json",
153-
path=TEST_DATA / "jsonschema" / "reduce_duplicate_field_types.json",
154-
),
164+
_file_example(
165+
"model-reuse.use-type-alias.schema",
166+
"model-reuse.md",
167+
language="json",
168+
path=TEST_DATA / "jsonschema" / "reduce_duplicate_field_types.json",
155169
),
156-
DocsExample(
157-
example_id="model-reuse.use-type-alias.output",
158-
path=DOCS / "model-reuse.md",
159-
render=lambda: render_file_example(
160-
language="python",
161-
path=EXPECTED_MAIN / "jsonschema" / "reduce_duplicate_field_types.py",
162-
strip_python_header=True,
163-
),
170+
_file_example(
171+
"model-reuse.use-type-alias.output",
172+
"model-reuse.md",
173+
language="python",
174+
path=EXPECTED_MAIN / "jsonschema" / "reduce_duplicate_field_types.py",
175+
strip_python_header=True,
164176
),
165177
)
166178

@@ -189,25 +201,37 @@ def iter_examples_by_path() -> Iterable[tuple[Path, tuple[DocsExample, ...]]]:
189201

190202
def update_docs_examples(*, check: bool = False) -> int:
191203
"""Update or check generated documentation examples."""
192-
changed_paths = []
204+
changed_content_by_path = {}
205+
out_of_date_paths = []
206+
missing_markers = False
193207
for path, examples in iter_examples_by_path():
194208
content = path.read_text(encoding="utf-8")
195209
updated_content = content
210+
missing_marker_for_path = False
196211
for example in examples:
197212
updated_content, found = replace_example(updated_content, example)
198213
if not found:
199-
return 1
214+
missing_markers = True
215+
missing_marker_for_path = True
216+
if missing_marker_for_path:
217+
continue
200218
if updated_content == content:
201219
continue
202220
if check:
203-
print(f"Docs examples are out of date: {path}", file=sys.stderr)
204-
print("Run 'python scripts/build_docs_examples.py' to update.", file=sys.stderr)
205-
return 1
206-
path.write_text(updated_content, encoding="utf-8")
207-
changed_paths.append(path)
208-
209-
if changed_paths:
210-
for path in changed_paths:
221+
out_of_date_paths.append(path)
222+
continue
223+
changed_content_by_path[path] = updated_content
224+
225+
for path in out_of_date_paths:
226+
print(f"Docs examples are out of date: {path}", file=sys.stderr)
227+
if out_of_date_paths:
228+
print("Run 'python scripts/build_docs_examples.py' to update.", file=sys.stderr)
229+
if missing_markers or out_of_date_paths:
230+
return 1
231+
232+
if changed_content_by_path:
233+
for path, updated_content in changed_content_by_path.items():
234+
path.write_text(updated_content, encoding="utf-8")
211235
print(f"Updated {path}")
212236
return 0
213237

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
<details>
3+
<summary>schema.yaml</summary>
4+
5+
```yaml
6+
name: User
7+
```
8+
</details>
9+
10+
```yaml
11+
name: User
12+
```
13+
14+
```python
15+
class User:
16+
pass
17+
```
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
return=0
2+
stdout:
3+
Docs examples are up to date.
4+
stderr:

tests/data/expected/docs_examples/reuse_scope_tree_example.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ models/
99
`-- shared.py
1010
```
1111

12+
**models/__init__.py:**
13+
_No code beyond the generated header._
14+
1215
**models/schema_a.py:**
1316

1417
```python
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
return=1
2+
stdout:
3+
stderr:
4+
Warning: markers not found for example.missing in <tmp>/missing.md
5+
Docs examples are out of date: <tmp>/outdated-one.md
6+
Docs examples are out of date: <tmp>/outdated-two.md
7+
Run 'python scripts/build_docs_examples.py' to update.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
return=0
2+
stdout:
3+
Updated <tmp>/doc.md
4+
stderr:
5+
content:
6+
Before
7+
<!-- BEGIN AUTO-GENERATED DOC EXAMPLE: example.write -->
8+
new
9+
<!-- END AUTO-GENERATED DOC EXAMPLE: example.write -->
10+
After

0 commit comments

Comments
 (0)