2
2
3
3
"""
4
4
.. currentmodule:: arraycontext
5
+
6
+ .. autoclass:: ExcludedField
5
7
.. autofunction:: dataclass_array_container
6
8
"""
7
9
31
33
"""
32
34
33
35
from dataclasses import fields
36
+
37
+ try :
38
+ # NOTE: mypy fails with `Module "typing" has no attribute "get_args"`
39
+ from typing import ( # type: ignore[attr-defined]
40
+ _AnnotatedAlias , _GenericAlias , get_args )
41
+ except ImportError :
42
+ from typing_extensions import ( # type: ignore[attr-defined]
43
+ _AnnotatedAlias , _GenericAlias , get_args )
44
+
34
45
from arraycontext .container import is_array_container_type
35
46
36
47
37
48
# {{{ dataclass containers
38
49
50
+ class ExcludedField :
51
+ """Can be used to annotate dataclass fields to be excluded from the container.
52
+
53
+ This can be done using :class:`typing.Annotated` as follows
54
+
55
+ .. code:: python
56
+
57
+ @dataclass
58
+ class MyClass:
59
+ x: np.ndarray
60
+ y: Annotated[np.ndarray, ExcludedField]
61
+ """
62
+
63
+
39
64
def dataclass_array_container (cls : type ) -> type :
40
- """A class decorator that makes the class to which it is applied a
65
+ """A class decorator that makes the class to which it is applied an
41
66
:class:`ArrayContainer` by registering appropriate implementations of
42
67
:func:`serialize_container` and :func:`deserialize_container`.
43
68
*cls* must be a :func:`~dataclasses.dataclass`.
44
69
45
70
Attributes that are not array containers are allowed. In order to decide
46
71
whether an attribute is an array container, the declared attribute type
47
72
is checked by the criteria from :func:`is_array_container_type`.
73
+
74
+ To explicitly exclude fields from the container serialization (that would
75
+ otherwise be recognized as array containers), use :class:`typing.Annotated`
76
+ using :class:`ExcludedField`.
48
77
"""
78
+
49
79
from dataclasses import is_dataclass , Field
50
80
assert is_dataclass (cls )
51
81
@@ -59,8 +89,12 @@ def is_array_field(f: Field) -> bool:
59
89
raise TypeError (
60
90
f"string annotation on field '{ f .name } ' not supported" )
61
91
62
- from typing import _SpecialForm
63
- if isinstance (f .type , _SpecialForm ):
92
+ # FIXME: is there a nicer way to recognize that we hit Annotated?
93
+ if isinstance (f .type , _AnnotatedAlias ):
94
+ if any (arg is ExcludedField for arg in get_args (f .type )):
95
+ return False
96
+
97
+ if isinstance (f .type , _GenericAlias ):
64
98
raise TypeError (
65
99
f"typing annotation not supported on field '{ f .name } ': "
66
100
f"'{ f .type !r} '" )
0 commit comments