-
-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add PostponedAnnotations configuration option (#653)
* Add ImportAnnotations to generator configuration This allows schemas having collisions between class members name and type to generate correct Python bindings, by enabling Postponed Evaluation of Annotations (see PEP 563). This is done by adding __future__.annotations as a default import to all generated modules when this option is enabled. * Add unit tests for ImportAnnotations option * Add integration test for ImportAnnotations option This test generates bindings having a collision between a class member name and its type, but uses the ImportAnnotations configuration option to postpone the evaluation of the type hint. The test then verifies that the binding can be loaded without errors, and that its colliding member can be accessed. * Add FAQ documentation for ImportAnnotations This helps users diagnose and fix name/type collisions by using the ImportAnnotations configuration option. * Add docs about minimum Python version for PEP 563 This informs users that enabling Postponed Evaluation of Annotations is only available for Python 3.7+. * Rename ImportAnnotations to PostponedAnnotations This commit renames references to the newly introduced feature to describe _what_ this feature does rather than _how_ it does it. * Add minimum Python version in CLI help * Skip test on unsupported Python versions * Add validation to generator output configuration This allows users to be informed that PostponedAnnotations are only available in Python 3.7+ by displaying a warning. * Trick pyupgrade into not deleting test code Extracting the Python version into a local variable prevents pyupgrade from refactoring version checks and changing the test semantics.
- Loading branch information
1 parent
7ca1d39
commit eb4b0f1
Showing
14 changed files
with
210 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,15 @@ | ||
Why I get a TypeError: requires a single type | ||
============================================= | ||
Why do I get a TypeError: requires a single type | ||
================================================ | ||
|
||
The full error message looks something like this: | ||
|
||
.. code-block:: | ||
TypeError: typing.Optional requires a single type. Got Field(name=None,type=None,default=<dataclasses._MISSING_TYPE object at 0x7f79f4b0d700>,default_facto. | ||
The error means the dataclass wrapper can't build the typing annotations for a model | ||
because the field type is ambiguous. If you are using the code generator make sure you | ||
are not using the same convention for both field and class names. | ||
This error means the typing annotations for a model are ambiguous because they collide with a class field. If you use Python 3.7 or later, you can set :code:`PostponedAnnotations` to :code:`true` in the :ref:`GeneratorOutput` section of the :ref:`generator config <Generator Config>` to solve this issue. This will enable Postponed Evaluations of Annotations (`PEP 563 <https://www.python.org/dev/peps/pep-0563/>`_) and the generated bindings will be able to be imported without errors. This feature is not available for Python 3.6 because :code:`annotations` were added to the :code:`__future__` module in Python 3.7.0b1. | ||
|
||
**Example** | ||
|
||
.. code-block:: python | ||
@dataclass | ||
class unit: | ||
pass | ||
@dataclass | ||
class element: | ||
unit: Optional[unit] = field() | ||
Read :ref:`more <Generator Config>` | ||
.. literalinclude:: /../tests/fixtures/annotations/model.py | ||
:language: python |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from tests.fixtures.annotations.model import ( | ||
Measurement, | ||
Weight, | ||
) | ||
from tests.fixtures.annotations.units import unit | ||
|
||
__all__ = [ | ||
"Measurement", | ||
"Weight", | ||
"unit", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from __future__ import annotations | ||
from dataclasses import dataclass, field | ||
from typing import Optional | ||
from tests.fixtures.annotations.units import unit | ||
|
||
__NAMESPACE__ = "http://domain.org/schema/model" | ||
|
||
|
||
@dataclass | ||
class Measurement: | ||
value: Optional[float] = field( | ||
default=None, | ||
metadata={ | ||
"required": True, | ||
} | ||
) | ||
unit: Optional[unit] = field( | ||
default=None, | ||
metadata={ | ||
"type": "Attribute", | ||
} | ||
) | ||
|
||
|
||
@dataclass | ||
class Weight(Measurement): | ||
class Meta: | ||
namespace = "http://domain.org/schema/model" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
<xs:schema targetNamespace="http://domain.org/schema/model" | ||
xmlns="http://domain.org/schema/model" | ||
xmlns:xs="http://www.w3.org/2001/XMLSchema" | ||
xmlns:utd="http://domain.org/schema/model/units"> | ||
|
||
<xs:import schemaLocation="./units.xsd"/> | ||
|
||
<xs:complexType name="Measurement"> | ||
<xs:simpleContent> | ||
<xs:extension base="xs:double"> | ||
<xs:attribute name="unit" type="utd:unit"/> | ||
</xs:extension> | ||
</xs:simpleContent> | ||
</xs:complexType> | ||
|
||
<xs:element name="Weight" type="Measurement"/> | ||
</xs:schema> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Weight unit="kg">2.0</Weight> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from __future__ import annotations | ||
from enum import Enum | ||
|
||
__NAMESPACE__ = "http://domain.org/schema/model/units" | ||
|
||
|
||
class unit(Enum): | ||
M = "m" | ||
KG = "kg" | ||
VALUE = "%" | ||
NA = "NA" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
<xs:schema targetNamespace="http://domain.org/schema/model/units" | ||
xmlns="http://domain.org/schema/model/units" | ||
xmlns:xs="http://www.w3.org/2001/XMLSchema"> | ||
|
||
<xs:simpleType name="stdUnit"> | ||
<xs:restriction base="xs:string"> | ||
<xs:enumeration value="m"/> | ||
<xs:enumeration value="kg"/> | ||
</xs:restriction> | ||
</xs:simpleType> | ||
|
||
<xs:simpleType name="miscUnit"> | ||
<xs:restriction base="xs:string"> | ||
<xs:enumeration value="%"/> | ||
<xs:enumeration value="NA"/> | ||
</xs:restriction> | ||
</xs:simpleType> | ||
|
||
<xs:simpleType name="unit"> | ||
<xs:union memberTypes="stdUnit miscUnit"/> | ||
</xs:simpleType> | ||
</xs:schema> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<Config xmlns="http://pypi.org/project/xsdata" version="22.1"> | ||
<Output maxLineLength="79"> | ||
<Package>tests.fixtures.annotations</Package> | ||
<Format repr="true" eq="true" order="false" unsafeHash="false" frozen="false" slots="false" kwOnly="false">dataclasses</Format> | ||
<Structure>filenames</Structure> | ||
<DocstringStyle>reStructuredText</DocstringStyle> | ||
<RelativeImports>false</RelativeImports> | ||
<CompoundFields defaultName="choice" forceDefaultName="false">true</CompoundFields> | ||
<PostponedAnnotations>true</PostponedAnnotations> | ||
</Output> | ||
<Conventions> | ||
<ClassName case="originalCase" safePrefix="type"/> | ||
<FieldName case="originalCase" safePrefix="value"/> | ||
<ConstantName case="screamingSnakeCase" safePrefix="value"/> | ||
<ModuleName case="snakeCase" safePrefix="mod"/> | ||
<PackageName case="originalCase" safePrefix="pkg"/> | ||
</Conventions> | ||
<Substitutions/> | ||
</Config> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import os | ||
import sys | ||
|
||
import pytest | ||
from click.testing import CliRunner | ||
|
||
from tests import fixtures_dir | ||
from tests import root | ||
from xsdata.cli import cli | ||
from xsdata.formats.dataclass.context import XmlContext | ||
from xsdata.formats.dataclass.parsers.xml import XmlParser | ||
from xsdata.utils.testing import load_class | ||
|
||
os.chdir(root) | ||
|
||
|
||
@pytest.mark.skipif(sys.version_info < (3, 7), reason="PEP563 introduced in 3.7") | ||
def test_annotations(): | ||
filepath = fixtures_dir.joinpath("annotations") | ||
schema = filepath.joinpath("model.xsd") | ||
runner = CliRunner() | ||
result = runner.invoke( | ||
cli, [str(schema), f"--config={str(filepath.joinpath('xsdata.xml'))}"] | ||
) | ||
|
||
if result.exception: | ||
raise result.exception | ||
|
||
try: | ||
Measurement = load_class(result.output, "Measurement") | ||
unit = load_class(result.output, "unit") | ||
except Exception: | ||
pytest.fail("Could not load class with member having the same name as type") | ||
|
||
filename = str(filepath.joinpath("sample.xml")) | ||
parser = XmlParser(context=XmlContext()) | ||
measurement = parser.parse(filename, Measurement) | ||
assert measurement.value == 2.0 | ||
assert measurement.unit == unit.KG |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters