Skip to content

Commit b0c4071

Browse files
committed
Update example, fix descriptions for top-level DC
Signed-off-by: Fabrice Normandin <[email protected]>
1 parent 14825f3 commit b0c4071

File tree

7 files changed

+98
-31
lines changed

7 files changed

+98
-31
lines changed

examples/config_files/.schemas/.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"properties": {
3+
"foo": {
4+
"default": 123,
5+
"title": "Foo",
6+
"type": "integer",
7+
"description": "A very important field."
8+
}
9+
},
10+
"title": "Bob",
11+
"type": "object",
12+
"description": "Some docstring."
13+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"$defs": {
3+
"Bob": {
4+
"properties": {
5+
"foo": {
6+
"default": 123,
7+
"title": "Foo",
8+
"type": "integer",
9+
"description": "A very important field."
10+
}
11+
},
12+
"title": "Bob",
13+
"type": "object",
14+
"description": "Some docstring."
15+
}
16+
},
17+
"properties": {
18+
"bob": {
19+
"$ref": "#/$defs/Bob",
20+
"description": "bobobobo."
21+
},
22+
"other_field": {
23+
"title": "Other Field",
24+
"type": "string",
25+
"description": "This is a docstring for the other field."
26+
}
27+
},
28+
"required": [
29+
"bob",
30+
"other_field"
31+
],
32+
"title": "Nested",
33+
"type": "object",
34+
"description": "Some docstring of the 'Nested' class."
35+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# yaml-language-server: $schema=.schemas/Bob_schema.json
2+
foo: 222
File renamed without changes.

examples/config_files/schema_example.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from dataclasses import dataclass
22
from pathlib import Path
33

4-
from simple_parsing.helpers.serialization.serializable import load_yaml
54
from simple_parsing.helpers.serialization.yaml_schema import save_yaml_with_schema
65

76

@@ -15,6 +14,8 @@ class Bob:
1514

1615
@dataclass
1716
class Nested:
17+
"""Some docstring of the 'Nested' class."""
18+
1819
bob: Bob # inline comment for field `bob` of class `Nested`
1920
"""bobobobo."""
2021

@@ -23,7 +24,12 @@ class Nested:
2324

2425

2526
if __name__ == "__main__":
27+
save_yaml_with_schema(
28+
Bob(foo=222),
29+
Path(__file__).parent / "bob_with_schema.yaml",
30+
)
31+
2632
save_yaml_with_schema(
2733
Nested(bob=Bob(foo=222), other_field="babab"),
28-
Path(__file__).parent / "nested.yaml",
34+
Path(__file__).parent / "nested_with_schema.yaml",
2935
)

simple_parsing/helpers/serialization/yaml_schema.py

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def save_yaml_with_schema(
4141
generated_schemas_dir = path.parent / ".schemas"
4242
generated_schemas_dir.mkdir(exist_ok=True, parents=True)
4343
schema_file = generated_schemas_dir / dc_schema_filename
44-
schema_file.write_text(json.dumps(json_schema, indent=2))
44+
schema_file.write_text(json.dumps(json_schema, indent=2) + "\n")
4545

4646
if repo_root:
4747
repo_root, _ = _try_make_relative(repo_root, relative_to=Path.cwd())
@@ -103,9 +103,9 @@ def save_yaml_with_schema_in_vscode_settings(
103103
_write_gitignore_file_for_schemas(generated_schemas_dir)
104104

105105
schema_file = generated_schemas_dir / dc_schema_filename
106-
schema_file.write_text(json.dumps(json_schema, indent=2))
106+
schema_file.write_text(json.dumps(json_schema, indent=2) + "\n")
107107

108-
# Alternatively: we can also use a setting in the VsCode editor to associate a schema file with
108+
# We can use a setting in the VsCode editor to associate a schema file with
109109
# a list of config files.
110110

111111
vscode_dir = repo_root / ".vscode"
@@ -119,21 +119,23 @@ def save_yaml_with_schema_in_vscode_settings(
119119
logger.error("Unable to load the vscode settings file!")
120120
raise
121121

122-
yaml_schemas: dict[str, str | list[str]] = vscode_settings.setdefault("yaml.schemas", {})
122+
yaml_schemas_setting: dict[str, str | list[str]] = vscode_settings.setdefault(
123+
"yaml.schemas", {}
124+
)
123125

124126
schema_key = str(schema_file.relative_to(repo_root))
125127
try:
126128
path_to_add = str(path.relative_to(repo_root))
127129
except ValueError:
128130
path_to_add = str(path)
129131

130-
files_associated_with_schema: str | list[str] = yaml_schemas.get(schema_key, [])
132+
files_associated_with_schema: str | list[str] = yaml_schemas_setting.get(schema_key, [])
131133
if isinstance(files_associated_with_schema, str):
132134
existing_value = files_associated_with_schema
133135
files_associated_with_schema = sorted(set([existing_value, path_to_add]))
134136
else:
135137
files_associated_with_schema = sorted(set(files_associated_with_schema + [path_to_add]))
136-
yaml_schemas[schema_key] = files_associated_with_schema
138+
yaml_schemas_setting[schema_key] = files_associated_with_schema
137139

138140
vscode_settings_file.write_text(json.dumps(vscode_settings, indent=2))
139141
return schema_file
@@ -173,6 +175,16 @@ def _has_default_dataclass_docstring(dc_type: type[Dataclass]) -> bool:
173175
return bool(docstring) and docstring.startswith(f"{dc_type.__name__}(")
174176

175177

178+
def _get_dc_type_with_name(dataclass_name: str) -> type[Dataclass] | None:
179+
# Get the dataclass type has this classname.
180+
frame = inspect.currentframe()
181+
assert frame
182+
for frame_info in inspect.getouterframes(frame):
183+
if is_dataclass_type(definition_dc_type := frame_info.frame.f_globals.get(dataclass_name)):
184+
return definition_dc_type
185+
return None
186+
187+
176188
def _update_schema_with_descriptions(
177189
dc: Dataclass,
178190
json_schema: PossiblyNestedDict[str, str | list[str]],
@@ -181,29 +193,29 @@ def _update_schema_with_descriptions(
181193
if not inplace:
182194
json_schema = copy.deepcopy(json_schema)
183195

184-
definitions = json_schema["$defs"]
185-
assert isinstance(definitions, dict)
186-
for classname, definition in definitions.items():
187-
if classname == type(dc).__name__:
188-
definition_dc_type = type(dc)
189-
else:
190-
# Get the dataclass type has this classname.
191-
frame = inspect.currentframe()
192-
assert frame
193-
outer_frames = inspect.getouterframes(frame)
194-
for frame in outer_frames:
195-
if classname in frame.frame.f_globals and is_dataclass_type(
196-
definition_dc_type := frame.frame.f_globals[classname]
197-
):
198-
break
196+
if "$defs" in json_schema:
197+
definitions = json_schema["$defs"]
198+
assert isinstance(definitions, dict)
199+
for classname, definition in definitions.items():
200+
if classname == type(dc).__name__:
201+
definition_dc_type = type(dc)
199202
else:
200-
logger.debug(
201-
f"Unable to find the dataclass type for {classname} in the caller globals."
202-
)
203-
continue
204-
205-
assert isinstance(definition, dict)
206-
_update_definition_in_schema_using_dc(definition, dc_type=definition_dc_type)
203+
# Get the dataclass type has this classname.
204+
frame = inspect.currentframe()
205+
assert frame
206+
definition_dc_type = _get_dc_type_with_name(classname)
207+
if not definition_dc_type:
208+
logger.debug(
209+
f"Unable to find the dataclass type for {classname} in the caller globals."
210+
f"Not adding descriptions for this dataclass."
211+
)
212+
continue
213+
214+
assert isinstance(definition, dict)
215+
_update_definition_in_schema_using_dc(definition, dc_type=definition_dc_type)
216+
217+
if "properties" in json_schema:
218+
_update_definition_in_schema_using_dc(json_schema, dc_type=type(dc))
207219

208220
return json_schema
209221

0 commit comments

Comments
 (0)