Skip to content

Commit

Permalink
Merge pull request #1082 from tefra/feat-1031
Browse files Browse the repository at this point in the history
Add cli config to use generic collections
  • Loading branch information
tefra authored Oct 21, 2024
2 parents 534857b + 73def4e commit dd587cd
Show file tree
Hide file tree
Showing 17 changed files with 107 additions and 96 deletions.
1 change: 0 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ jobs:
fail-fast: false
matrix:
include:
- {name: Python 3.8, python: '3.8'}
- {name: Python 3.9, python: '3.9'}
- {name: Python 3.10, python: '3.10'}
- {name: Python 3.11, python: '3.11'}
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ exclude: tests/fixtures

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: debug-statements
- repo: https://github.com/crate-ci/typos
rev: v1.24.6
rev: v1.26.0
hooks:
- id: typos
exclude: ^tests/|.xsd|xsdata/models/datatype.py$
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.6
rev: v0.7.0
hooks:
- id: ruff
args: [ --fix, --show-fixes]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
rev: v1.12.1
hooks:
- id: mypy
files: ^(xsdata/)
Expand Down
9 changes: 8 additions & 1 deletion docs/codegen/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@ type # typing.Type

**CLI Option:** `--subscriptable-types / --no-subscriptable-types`

**Requirements:** `python>=3.9`
### `genericCollections`

Use `collections.abc.Iterable` and `collections.abc.Mapping` instead of `List|Tuple` and
`Dict`

**Default Value:** `False`

**CLI Option:** `--generic-collections / --no-generic-collections`

### `unionType`

Expand Down
2 changes: 1 addition & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ $ xsdata --help

## Requirements

!!! Note "xsData relies on these awesome libraries and supports `python >= 3.8`"
!!! Note "xsData relies on these awesome libraries and supports `python >= 3.9`"

- [lxml](https://lxml.de/) - XML advanced features
- [requests](https://requests.readthedocs.io/) - Webservice Default Transport
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -29,7 +28,7 @@ classifiers = [
"Topic :: Text Processing :: Markup :: XML",
]
keywords = ["xsd", "wsdl", "schema", "dtd", "binding", "xml", "json", "dataclasses", "generator", "cli"]
requires-python = ">=3.8"
requires-python = ">=3.9"
dependencies = [
"typing-extensions>=4.7.0",
]
Expand Down
1 change: 0 additions & 1 deletion tests/formats/dataclass/cases/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import sys

PY39 = sys.version_info[:2] >= (3, 9)
PY310 = sys.version_info[:2] >= (3, 10)
24 changes: 10 additions & 14 deletions tests/formats/dataclass/cases/attribute.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Iterable
from typing import Dict, List, Literal, Optional, Set, Tuple, Union

from tests.formats.dataclass.cases import PY39, PY310
from tests.formats.dataclass.cases import PY310
from xsdata.models.enums import Mode

tokens = [
Expand All @@ -12,9 +13,17 @@
(List[Union[List[int], int]], False),
(List[List[int]], False),
(Tuple[int, ...], ((int,), None, tuple)),
(Iterable[int], ((int,), None, list)),
(List[int], ((int,), None, list)),
(List[Union[str, int]], ((str, int), None, list)),
(Optional[List[Union[str, int]]], ((str, int), None, list)),
(list[int, int], False),
(dict[str, str], False),
(dict, False),
(set[str], False),
(tuple[int, ...], ((int,), None, tuple)),
(list[int], ((int,), None, list)),
(list[Union[str, int]], ((str, int), None, list)),
]

not_tokens = [
Expand All @@ -25,19 +34,6 @@
(Union[str, Mode], ((str, Mode), None, None)),
]

if PY39:
tokens.extend(
[
(list[int, int], False),
(dict[str, str], False),
(dict, False),
(set[str], False),
(tuple[int, ...], ((int,), None, tuple)),
(list[int], ((int,), None, list)),
(list[Union[str, int]], ((str, int), None, list)),
]
)

if PY310:
tokens.extend(
[
Expand Down
12 changes: 3 additions & 9 deletions tests/formats/dataclass/cases/attributes.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from collections.abc import Mapping
from typing import Dict, List, Set, Tuple

from tests.formats.dataclass.cases import PY39

cases = [
(int, False),
(Set, False),
Expand All @@ -10,11 +9,6 @@
(Dict[str, int], False),
(Dict, ((str,), dict, None)),
(Dict[str, str], ((str,), dict, None)),
(Mapping[str, str], ((str,), dict, None)),
(dict[str, str], ((str,), dict, None)),
]

if PY39:
cases.extend(
[
(dict[str, str], ((str,), dict, None)),
]
)
29 changes: 9 additions & 20 deletions tests/formats/dataclass/cases/element.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from collections.abc import Iterable
from typing import Dict, List, Optional, Set, Tuple, Union

from tests.formats.dataclass.cases import PY39

tokens = [
(Set, False),
(Dict[str, int], False),
Expand All @@ -12,8 +11,14 @@
(List[List[str]], ((str,), list, list)),
(Optional[List[List[Union[str, int]]]], ((str, int), list, list)),
(List[Tuple[str, ...]], ((str,), list, tuple)),
(Iterable[Iterable[str, ...]], ((str,), list, list)),
(Tuple[List[str], ...], ((str,), tuple, list)),
(Optional[Tuple[List[str], ...]], ((str,), tuple, list)),
(list[str], ((str,), None, list)),
(tuple[str, ...], ((str,), None, tuple)),
(list[list[str]], ((str,), list, list)),
(list[tuple[str, ...]], ((str,), list, tuple)),
(tuple[list[str], ...], ((str,), tuple, list)),
]

not_tokens = [
Expand All @@ -28,22 +33,6 @@
(List[Union[str, int]], ((str, int), list, None)),
(Optional[List[Union[str, int]]], ((str, int), list, None)),
(Tuple[str, ...], ((str,), tuple, None)),
(list[str], ((str,), list, None)),
(tuple[str, ...], ((str,), tuple, None)),
]

if PY39:
tokens.extend(
[
(list[str], ((str,), None, list)),
(tuple[str, ...], ((str,), None, tuple)),
(list[list[str]], ((str,), list, list)),
(list[tuple[str, ...]], ((str,), list, tuple)),
(tuple[list[str], ...], ((str,), tuple, list)),
]
)

not_tokens.extend(
[
(list[str], ((str,), list, None)),
(tuple[str, ...], ((str,), tuple, None)),
]
)
15 changes: 4 additions & 11 deletions tests/formats/dataclass/cases/elements.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Dict, List, Optional, Tuple, Union

from tests.formats.dataclass.cases import PY39, PY310
from tests.formats.dataclass.cases import PY310

cases = [
(Dict, False),
Expand All @@ -10,19 +10,12 @@
(Optional[Union[str, int]], ((object,), None, None)),
(Union[str, int, None], ((object,), None, None)),
(List[Union[List[str], Tuple[str, ...]]], ((object,), list, None)),
(list[str], ((object,), list, None)),
(tuple[str, ...], ((object,), tuple, None)),
(list[Union[list[str], tuple[str, ...]]], ((object,), list, None)),
]


if PY39:
cases.extend(
[
(list[str], ((object,), list, None)),
(tuple[str, ...], ((object,), tuple, None)),
(list[Union[list[str], tuple[str, ...]]], ((object,), list, None)),
]
)


if PY310:
cases.extend(
[
Expand Down
2 changes: 2 additions & 0 deletions tests/formats/dataclass/cases/wildcard.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from collections.abc import Iterable
from typing import Dict, List, Literal, Optional, Set, Tuple

from tests.formats.dataclass.cases import PY310
Expand All @@ -11,6 +12,7 @@
(object, ((object,), None, None)),
(List[object], ((object,), list, None)),
(Tuple[object, ...], ((object,), tuple, None)),
(Iterable[object, ...], ((object,), list, None)),
(Optional[object], ((object,), None, None)),
]

Expand Down
14 changes: 14 additions & 0 deletions tests/formats/dataclass/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,9 @@ def test_field_type_with_array_type(self):
self.filters.format.frozen = False
self.assertEqual('list["A.B.C"]', self.filters.field_type(self.obj, attr))

self.filters.generic_collections = True
self.assertEqual('Iterable["A.B.C"]', self.filters.field_type(self.obj, attr))

def test_field_type_with_token_attr(self):
attr = AttrFactory.create(
types=AttrTypeFactory.list(1, qname="foo_bar"),
Expand Down Expand Up @@ -710,6 +713,9 @@ def test_field_type_with_any_attribute(self):
self.filters.subscriptable_types = True
self.assertEqual("dict[str, str]", self.filters.field_type(self.obj, attr))

self.filters.generic_collections = True
self.assertEqual("Mapping[str, str]", self.filters.field_type(self.obj, attr))

def test_field_type_with_native_type(self):
attr = AttrFactory.create(
types=[
Expand Down Expand Up @@ -903,6 +909,14 @@ def test_default_imports_with_typing(self):
expected = "from typing import Any"
self.assertIn(expected, self.filters.default_imports(output))

output = ": Iterable[str] = "
expected = "from collections.abc import Iterable"
self.assertIn(expected, self.filters.default_imports(output))

output = ": Mapping[str, str] = "
expected = "from collections.abc import Mapping"
self.assertIn(expected, self.filters.default_imports(output))

def test_default_imports_combo(self):
output = (
"@dataclass\n"
Expand Down
33 changes: 14 additions & 19 deletions tests/models/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def test_create(self):
expected = (
'<?xml version="1.0" encoding="UTF-8"?>\n'
f'<Config xmlns="http://pypi.org/project/xsdata" version="{__version__}">\n'
' <Output maxLineLength="79" subscriptableTypes="false" unionType="false">\n'
' <Output maxLineLength="79" subscriptableTypes="false" genericCollections="false" unionType="false">\n'
" <Package>generated</Package>\n"
' <Format repr="true" eq="true" order="false" unsafeHash="false" frozen="false" slots="false" kwOnly="false">dataclasses</Format>\n'
" <Structure>filenames</Structure>\n"
Expand Down Expand Up @@ -89,7 +89,7 @@ def test_read(self):
expected = (
'<?xml version="1.0" encoding="UTF-8"?>\n'
f'<Config xmlns="http://pypi.org/project/xsdata" version="{__version__}">\n'
' <Output maxLineLength="79" subscriptableTypes="false" unionType="false">\n'
' <Output maxLineLength="79" subscriptableTypes="false" genericCollections="false" unionType="false">\n'
" <Package>foo.bar</Package>\n"
' <Format repr="true" eq="true" order="false" unsafeHash="false"'
' frozen="false" slots="false" kwOnly="false">dataclasses</Format>\n'
Expand Down Expand Up @@ -136,23 +136,6 @@ def test_format_with_invalid_eq_config(self):

self.assertEqual("Enabling eq because order is true", str(w[-1].message))

def test_subscriptable_types_requires_390(self):
if sys.version_info < (3, 9):
with warnings.catch_warnings(record=True) as w:
self.assertFalse(
GeneratorOutput(subscriptable_types=True).subscriptable_types
)

self.assertEqual(
"Generics PEP 585 requires python >= 3.9, reverting...",
str(w[-1].message),
)

else:
self.assertTrue(
GeneratorOutput(subscriptable_types=True).subscriptable_types
)

def test_use_union_type_requires_310_and_postponed_annotations(self):
if sys.version_info < (3, 10):
with warnings.catch_warnings(record=True) as w:
Expand All @@ -172,6 +155,18 @@ def test_use_union_type_requires_310_and_postponed_annotations(self):
str(w[-1].message),
)

def test_generic_collections_requires_frozen_false(self):
with warnings.catch_warnings(record=True) as w:
output = GeneratorOutput(
generic_collections=True, format=OutputFormat(frozen=True)
)
self.assertFalse(output.generic_collections)

self.assertEqual(
"Generic Collections, requires frozen=False, reverting...",
str(w[-1].message),
)

def test_format_slots_requires_310(self):
if sys.version_info < (3, 10):
self.assertTrue(OutputFormat(slots=True, value="attrs").slots)
Expand Down
2 changes: 1 addition & 1 deletion xsdata/formats/dataclass/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def from_service(cls, obj: Any, **kwargs: Any) -> "Config":
for f in fields(cls)
}

return cls(**params)
return cls(**params) # type: ignore


class TransportTypes:
Expand Down
Loading

0 comments on commit dd587cd

Please sign in to comment.