Skip to content

Commit eb32a94

Browse files
committed
Pydantic: fix datetime imports; fix Null fields in pydantic
1 parent 7d398ac commit eb32a94

File tree

6 files changed

+40
-8
lines changed

6 files changed

+40
-8
lines changed

json_to_models/dynamic_typing/typing.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import operator
2+
from datetime import date, datetime, time
23
from inspect import isclass
34
from typing import Dict, Set, Tuple
45

@@ -16,7 +17,10 @@ def metadata_to_typing(t: MetaData) -> Tuple[ImportPathList, str]:
1617
if issubclass(t, StringSerializable):
1718
return t.to_typing_code()
1819
else:
19-
return ([], t.__name__)
20+
imports = []
21+
if issubclass(t, (date, datetime, time)):
22+
imports.append((t.__module__, [t.__name__]))
23+
return (imports, t.__name__)
2024
elif isinstance(t, dict):
2125
raise ValueError("Can not convert dict instance to typing code. It should be wrapped into ModelMeta instance")
2226
else:

json_to_models/models/base.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,16 @@ def fields(self) -> Tuple[ImportPathList, List[str]]:
152152
imports: ImportPathList = []
153153
strings: List[str] = []
154154
for is_optional, fields in enumerate((required, optional)):
155+
fields = self._filter_fields(fields)
155156
for field in fields:
156157
field_imports, data = self.field_data(field, self.model.type[field], bool(is_optional))
157158
imports.extend(field_imports)
158159
strings.append(self.FIELD.render(**data))
159160
return imports, strings
160161

162+
def _filter_fields(self, fields):
163+
return fields
164+
161165
@property
162166
def string_field_paths(self) -> List[str]:
163167
"""

json_to_models/models/pydantic.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from typing import List, Optional, Tuple
33

44
from .base import GenericModelCodeGenerator, KWAGRS_TEMPLATE, sort_kwargs, template
5-
from ..dynamic_typing import BaseType, DDict, DList, DOptional, ImportPathList, MetaData, ModelMeta, StringSerializable
5+
from ..dynamic_typing import BaseType, DDict, DList, DOptional, ImportPathList, MetaData, ModelMeta, Null, StringSerializable, Unknown
66

77
DEFAULT_ORDER = (
88
"*",
@@ -32,6 +32,16 @@ def generate(self, nested_classes: List[str] = None, extra: str = "", **kwargs)
3232
imports.append(('pydantic', ['BaseModel', 'Field']))
3333
return imports, body
3434

35+
def _filter_fields(self, fields):
36+
fields = super()._filter_fields(fields)
37+
filtered = []
38+
for field in fields:
39+
field_type = self.model.type[field]
40+
if field_type in (Unknown, Null):
41+
continue
42+
filtered.append(field)
43+
return filtered
44+
3545
def field_data(self, name: str, meta: MetaData, optional: bool) -> Tuple[ImportPathList, dict]:
3646
"""
3747
Form field data for template

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ def run_tests(self):
5050
},
5151
install_requires=required,
5252
cmdclass={"test": PyTest},
53-
tests_require=["pytest>=4.4.0", "pytest-xdist", "requests", "attrs"],
53+
tests_require=["pytest>=4.4.0", "pytest-xdist", "requests", "attrs", "pydantic>=1.3"],
5454
data_files=[('', ['requirements.txt', 'pytest.ini', '.coveragerc', 'LICENSE', 'README.md', 'CHANGELOG.md'])]
5555
)

test/test_cli/test_script.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ def _validate_result(proc: subprocess.Popen, output=None, output_file: Path = No
9898
# Note: imp package is deprecated but I can't find a way to create dummy module using importlib
9999
module = imp.new_module("test_model")
100100
sys.modules["test_model"] = module
101-
exec(compile(stdout, "test_model.py", "exec"), module.__dict__)
101+
try:
102+
exec(compile(stdout, "test_model.py", "exec"), module.__dict__)
103+
except Exception as e:
104+
assert not e, stdout
102105
return stdout, stderr
103106

104107

@@ -126,6 +129,17 @@ def test_script_attrs(command):
126129
print(stdout)
127130

128131

132+
@pytest.mark.parametrize("command", test_commands)
133+
def test_script_pydantic(command):
134+
command += " -f pydantic"
135+
# Pydantic has native (str) -> (builtin_type) converters
136+
command = command.replace('--strings-converters', '')
137+
proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
138+
stdout, stderr = _validate_result(proc)
139+
assert "(BaseModel):" in stdout
140+
print(stdout)
141+
142+
129143
@pytest.mark.parametrize("command", test_commands)
130144
def test_script_dataclasses(command):
131145
command += " -f dataclasses"

testing_tools/real_apis/f1.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
from json_to_models.dynamic_typing import register_datetime_classes
99
from json_to_models.generator import MetadataGenerator
1010
from json_to_models.models.base import generate_code
11-
from json_to_models.models.dataclasses import DataclassModelCodeGenerator
12-
from json_to_models.models.structure import compose_models
11+
from json_to_models.models.pydantic import PydanticModelCodeGenerator
12+
from json_to_models.models.structure import compose_models_flat
1313
from json_to_models.registry import ModelRegistry
1414
from testing_tools.pprint_meta_data import pretty_format_meta
1515
from testing_tools.real_apis import dump_response
@@ -56,11 +56,11 @@ def main():
5656
print(pretty_format_meta(model))
5757
print("=" * 20, end='')
5858

59-
structure = compose_models(reg.models_map)
59+
structure = compose_models_flat(reg.models_map)
6060
# print('\n', json_format([structure[0], {str(a): str(b) for a, b in structure[1].items()}]))
6161
# print("=" * 20)
6262

63-
print(generate_code(structure, DataclassModelCodeGenerator, class_generator_kwargs={"post_init_converters": True}))
63+
print(generate_code(structure, PydanticModelCodeGenerator, class_generator_kwargs={}))
6464

6565

6666
if __name__ == '__main__':

0 commit comments

Comments
 (0)