From 8df715871860ae49fbbd47bbacc93e5a03126477 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Fri, 25 Dec 2020 18:17:43 +0900 Subject: [PATCH] fix array include null (#288) --- .../model/pydantic/types.py | 2 +- datamodel_code_generator/types.py | 13 +++++++++- .../main_json_array_include_null/output.py | 17 +++++++++++++ tests/data/json/array_include_null.json | 10 ++++++++ tests/test_main.py | 25 +++++++++++++++++++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/data/expected/main/main_json_array_include_null/output.py create mode 100644 tests/data/json/array_include_null.json diff --git a/datamodel_code_generator/model/pydantic/types.py b/datamodel_code_generator/model/pydantic/types.py index 109488306..a6e656846 100644 --- a/datamodel_code_generator/model/pydantic/types.py +++ b/datamodel_code_generator/model/pydantic/types.py @@ -71,7 +71,7 @@ Types.ipv6: DataType(type='IPv6Address', imports_=[IMPORT_IPV6ADDRESS]), Types.boolean: DataType(type='bool'), Types.object: DataType(type='Dict[str, Any]', imports_=[IMPORT_ANY, IMPORT_DICT,],), - Types.null: DataType(type='Any', imports_=[IMPORT_ANY]), + Types.null: DataType(type='Any', imports_=[IMPORT_ANY], is_optional=True), Types.array: DataType(type='List[Any]', imports_=[IMPORT_LIST, IMPORT_ANY]), Types.any: DataType(type='Any', imports_=[IMPORT_ANY]), } diff --git a/datamodel_code_generator/types.py b/datamodel_code_generator/types.py index 2528f3819..bf641e708 100644 --- a/datamodel_code_generator/types.py +++ b/datamodel_code_generator/types.py @@ -52,6 +52,17 @@ def __init__(self, **values: Any) -> None: super().__init__(**values) if self.type and (self.reference or self.ref): self.unresolved_types.add(self.type) + for type_ in self.data_types: + if type_.type == 'Any' and type_.is_optional: + if any(t for t in self.data_types if t.type != 'Any'): + self.is_optional = True + self.data_types = [ + t + for t in self.data_types + if not (t.type == 'Any' and t.is_optional) + ] + break + imports: Tuple[Tuple[bool, Import], ...] = ( (self.is_optional, IMPORT_OPTIONAL), (len(self.data_types) > 1, IMPORT_UNION), @@ -100,7 +111,7 @@ def type_hint(self) -> str: else: dict_ = 'Dict' type_ = f'{dict_}[str, {type_}]' if type_ else 'dict_' - if self.is_optional: + if self.is_optional and type_ != 'Any': type_ = f'Optional[{type_}]' if self.is_func: if self.kwargs: diff --git a/tests/data/expected/main/main_json_array_include_null/output.py b/tests/data/expected/main/main_json_array_include_null/output.py new file mode 100644 index 000000000..8ae16c871 --- /dev/null +++ b/tests/data/expected/main/main_json_array_include_null/output.py @@ -0,0 +1,17 @@ +# generated by datamodel-codegen: +# filename: array_include_null.json +# timestamp: 2019-07-26T00:00:00+00:00 + +from __future__ import annotations + +from typing import List, Optional + +from pydantic import BaseModel + + +class Item(BaseModel): + oofield: Optional[List[int]] + + +class Model(BaseModel): + items: List[Item] diff --git a/tests/data/json/array_include_null.json b/tests/data/json/array_include_null.json new file mode 100644 index 000000000..75ddf1d44 --- /dev/null +++ b/tests/data/json/array_include_null.json @@ -0,0 +1,10 @@ +{ + "items": [ + { + "oofield": null + }, + { + "oofield": [1, 2, 3] + } + ] +} \ No newline at end of file diff --git a/tests/test_main.py b/tests/test_main.py index 34ea93254..ad81eec94 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -331,6 +331,31 @@ def test_main_json_failed(): main() +@freeze_time('2019-07-26') +def test_main_json_arrary_include_null(): + with TemporaryDirectory() as output_dir: + output_file: Path = Path(output_dir) / 'output.py' + return_code: Exit = main( + [ + '--input', + str(JSON_DATA_PATH / 'array_include_null.json'), + '--output', + str(output_file), + '--input-file-type', + 'json', + ] + ) + assert return_code == Exit.OK + assert ( + output_file.read_text() + == ( + EXPECTED_MAIN_PATH / 'main_json_array_include_null' / 'output.py' + ).read_text() + ) + with pytest.raises(SystemExit): + main() + + @freeze_time('2019-07-26') def test_main_null_and_array(): with TemporaryDirectory() as output_dir: