Skip to content

Commit

Permalink
feat: Generate ForwardRef() instead of Type[]
Browse files Browse the repository at this point in the history
  • Loading branch information
tefra committed Apr 14, 2024
1 parent 875ef19 commit 762ecb8
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 30 deletions.
2 changes: 0 additions & 2 deletions docs/models/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ collections are also supported.
| Case | Example |
| -------------- | --------------------------------------------------------------------------------- |
| List | `value: List[str] = field(default_factory=list)` |
| Optional List | `value: Optional[List[str]] = field(default=None)` |
| List Union | `value: List[Union[str, int]] = field(default_factory=list)` |
| Tokens List | `value: List[str] = field(default_factory=list, metadata={"tokens": True})` |
| List of Tokens | `value: List[List[str]] = field(default_factory=list, metadata={"tokens": True})` |
Expand All @@ -37,7 +36,6 @@ collections are also supported.
| Case | Example |
| --------------- | ---------------------------------------------------------------------------------------------- |
| Tuple | `value: Tuple[str, ...] = field(default_factory=tuple)` |
| Optional Tuple | `value: Optional[Tuple[str, ...]] = field(default=None)` |
| Tuple Union | `value: Tuple[Union[str, int], ...] = field(default_factory=tuple)` |
| Tokens Tuple | `value: Tuple[str, ...] = field(default_factory=tuple, metadata={"tokens": True})` |
| Tuple of Tokens | `value: Tuple[Tuple[str, ...], ...] = field(default_factory=tuple, metadata={"tokens": True})` |
Expand Down
30 changes: 9 additions & 21 deletions tests/formats/dataclass/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,40 +770,32 @@ def test_choice_type(self):
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual("Type[Foobar]", actual)

self.filters.subscriptable_types = True
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual("type[Foobar]", actual)

def test_choice_type_with_forward_reference(self):
choice = AttrFactory.create(
types=[AttrTypeFactory.create("foobar", forward=True)]
)
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual('Type["A.B.Foobar"]', actual)
self.assertEqual('ForwardRef("A.B.Foobar")', actual)

def test_choice_type_with_circular_reference(self):
choice = AttrFactory.create(
types=[AttrTypeFactory.create("foobar", circular=True)]
)
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual('Type["Foobar"]', actual)
self.assertEqual('ForwardRef("Foobar")', actual)

self.filters.postponed_annotations = True
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual('Type["Foobar"]', actual)
self.assertEqual('ForwardRef("Foobar")', actual)

def test_choice_type_with_multiple_types(self):
choice = AttrFactory.create(types=[type_str, type_bool])
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual("Type[Union[str, bool]]", actual)

self.filters.subscriptable_types = True
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual("type[Union[str, bool]]", actual)

self.filters.union_type = True
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual("type[str | bool]", actual)
self.assertEqual("Type[str | bool]", actual)

def test_choice_type_with_list_types_are_ignored(self):
choice = AttrFactory.create(types=[type_str, type_bool])
Expand All @@ -824,7 +816,7 @@ def test_choice_type_with_restrictions_tokens_true(self):
self.filters.union_type = True
self.filters.subscriptable_types = True
actual = self.filters.choice_type(choice, ["a", "b"])
self.assertEqual("type[tuple[str | bool, ...]]", actual)
self.assertEqual("Type[tuple[str | bool, ...]]", actual)

def test_default_imports_with_decimal(self):
expected = "from decimal import Decimal"
Expand Down Expand Up @@ -900,8 +892,8 @@ def test_default_imports_with_typing(self):
expected = "from typing import Union"
self.assertIn(expected, self.filters.default_imports(output))

output = " Type["
expected = "from typing import Type"
output = ": ForwardRef("
expected = "from typing import ForwardRef"
self.assertIn(expected, self.filters.default_imports(output))

output = ": Any = "
Expand Down Expand Up @@ -946,8 +938,7 @@ def test_format_metadata(self):
"level_two": {"a": 1},
"list": [
{"type": "Type[object]"},
{"type": 'type["something"]'},
{"type": "Type[object] mpla"},
{"type": 'ForwardRef("something")'},
],
"default": "1",
"default_factory": "list",
Expand All @@ -969,10 +960,7 @@ def test_format_metadata(self):
' "type": object,\n'
" },\n"
" {\n"
' "type": type["something"],\n'
" },\n"
" {\n"
' "type": "Type[object] mpla",\n'
' "type": ForwardRef("something"),\n'
" },\n"
" ],\n"
' "default": 1,\n'
Expand Down
15 changes: 8 additions & 7 deletions xsdata/formats/dataclass/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,10 +557,11 @@ def format_string(self, data: str, indent: int, key: str = "", pad: int = 0) ->
length and the additional pad is more than the max line length,
wrap the text into multiple lines, avoiding breaking long words
"""
if (data.startswith("Type[") or data.startswith("type[")) and data.endswith(
"]"
):
return data if data[5] == '"' else data[5:-1]
if data.startswith("ForwardRef("):
return data

if data.startswith("Type["):
return data[5:-1]

if data.startswith("Literal[") and data.endswith("]"):
return data[8:-1]
Expand Down Expand Up @@ -822,8 +823,8 @@ def choice_type(self, choice: Attr, parents: List[str]) -> str:
iterable_fmt = self._get_iterable_format()
result = iterable_fmt.format(result)

if self.subscriptable_types:
return f"type[{result}]"
if result.startswith('"'):
return f"ForwardRef({result})"

return f"Type[{result}]"

Expand Down Expand Up @@ -915,8 +916,8 @@ def build_import_patterns(cls) -> Dict[str, Dict]:
"List": [": List["],
"Optional": ["Optional["],
"Tuple": ["Tuple["],
"Type": ["Type["],
"Union": ["Union["],
"ForwardRef": [": ForwardRef("],
"Any": type_patterns("Any"),
},
"xml.etree.ElementTree": {"QName": type_patterns("QName")},
Expand Down

0 comments on commit 762ecb8

Please sign in to comment.