1212 import attr ._make
1313
1414
15+ T = t .TypeVar ("T" )
16+
17+
1518def schema (
16- cls : t . Type , many : bool = False , meta : t .Dict [str , t .Any ] = {}
19+ cls : type , many : bool = False , meta : t .Dict [str , t .Any ] = {}
1720) -> marshmallow .Schema :
1821 """Build a marshmallow schema *instance* for the class.
1922
@@ -29,7 +32,7 @@ def schema(
2932
3033
3134def schema_class (
32- cls : t . Type , meta : t .Dict [str , t .Any ] = {}
35+ cls : type , meta : t .Dict [str , t .Any ] = {}
3336) -> t .Type [marshmallow .Schema ]:
3437 """Build a marshmallow schema *class* for the class.
3538
@@ -46,7 +49,7 @@ def schema_class(
4649
4750def metadata (
4851 field : marshmallow .fields .Field ,
49- ) -> t .Dict ["desert._make._DesertSentinel" , t . Dict [ t . Any , marshmallow . fields . Field ] ]:
52+ ) -> t .Dict [object , object ]:
5053 """Specify a marshmallow field in the field metadata.
5154
5255 .. code-block:: python
@@ -56,7 +59,53 @@ def metadata(
5659 return {desert ._make ._DESERT_SENTINEL : {"marshmallow_field" : field }}
5760
5861
59- def field (marshmallow_field : marshmallow .fields .Field , ** kw ) -> dataclasses .Field :
62+ # TODO: maybe deprecate and rename metadata()
63+ create_metadata = metadata
64+
65+
66+ # These overloads lie about their return type just as both attrs and dataclasses
67+ # do so as to support the normal usage of `attribute: int = field()`
68+ @t .overload
69+ def field (
70+ marshmallow_field : marshmallow .fields .Field ,
71+ * ,
72+ default : T ,
73+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
74+ ** kw : object ,
75+ ) -> T :
76+ ...
77+
78+
79+ @t .overload
80+ def field (
81+ marshmallow_field : marshmallow .fields .Field ,
82+ * ,
83+ default_factory : t .Callable [[], T ],
84+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
85+ ** kw : object ,
86+ ) -> T :
87+ ...
88+
89+
90+ @t .overload
91+ def field (
92+ marshmallow_field : marshmallow .fields .Field ,
93+ * ,
94+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
95+ ** kw : object ,
96+ ) -> object :
97+ ...
98+
99+
100+ # The return type hint of object is certainly a lie but fits a lot better with
101+ # the normal use of `x: int = desert.field()`. Both dataclasses and attrs
102+ # prioritize hinting for this usage as well. Perhaps someday we'll have a
103+ # plugin that indicates the actual type.
104+ def field (
105+ marshmallow_field : marshmallow .fields .Field ,
106+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
107+ ** kw : object ,
108+ ) -> object :
60109 """Specify a marshmallow field in the metadata for a ``dataclasses.dataclass``.
61110
62111 .. code-block:: python
@@ -65,15 +114,61 @@ def field(marshmallow_field: marshmallow.fields.Field, **kw) -> dataclasses.Fiel
65114 class A:
66115 x: int = desert.field(marshmallow.fields.Int())
67116 """
68- meta = metadata (marshmallow_field )
69- meta .update (kw .pop ("metadata" , {}))
70- # typeshed hints it as Mapping[str, Any] without any obvious reason
71- # https://github.com/python/typeshed/blob/95a45eb4abd0c25849268983cb614e3bf6b9b264/stdlib/dataclasses.pyi#L81
72- # https://github.com/python/typeshed/pull/5823
73- return dataclasses .field (** kw , metadata = meta ) # type: ignore[arg-type]
74-
75-
76- def ib (marshmallow_field : marshmallow .fields .Field , ** kw ) -> attr ._make ._CountingAttr :
117+ if metadata is None :
118+ metadata = {}
119+
120+ meta : t .Dict [object , object ] = create_metadata (marshmallow_field )
121+ meta .update (metadata )
122+
123+ # call-overload and new_field intermediary:
124+ # https://github.com/python/typeshed/pull/5823
125+ new_field : dataclasses .Field [object ] = dataclasses .field (** kw , metadata = meta ) # type: ignore[call-overload]
126+ return new_field
127+
128+
129+ # These overloads lie about their return type just as both attrs and dataclasses
130+ # do so as to support the normal usage of `attribute: int = field()`
131+ @t .overload
132+ def ib (
133+ marshmallow_field : marshmallow .fields .Field ,
134+ * ,
135+ default : t .Union [T , t .Callable [[], T ]],
136+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
137+ ** kw : object ,
138+ ) -> T :
139+ ...
140+
141+
142+ @t .overload
143+ def ib (
144+ marshmallow_field : marshmallow .fields .Field ,
145+ * ,
146+ factory : t .Callable [[], T ],
147+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
148+ ** kw : object ,
149+ ) -> T :
150+ ...
151+
152+
153+ @t .overload
154+ def ib (
155+ marshmallow_field : marshmallow .fields .Field ,
156+ * ,
157+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
158+ ** kw : object ,
159+ ) -> object :
160+ ...
161+
162+
163+ # The return type hint of object is certainly a lie but fits a lot better with
164+ # the normal use of `x: int = desert.ib()`. Both dataclasses and attrs
165+ # prioritize hinting for this usage as well. Perhaps someday we'll have a
166+ # plugin that indicates the actual type.
167+ def ib (
168+ marshmallow_field : marshmallow .fields .Field ,
169+ metadata : t .Optional [t .Mapping [object , object ]] = None ,
170+ ** kw : object ,
171+ ) -> object :
77172 """Specify a marshmallow field in the metadata for an ``attr.dataclass``.
78173
79174 .. code-block:: python
@@ -82,9 +177,13 @@ def ib(marshmallow_field: marshmallow.fields.Field, **kw) -> attr._make._Countin
82177 class A:
83178 x: int = desert.ib(marshmallow.fields.Int())
84179 """
85- meta = metadata (marshmallow_field )
86- meta .update (kw .pop ("metadata" , {}))
87- return attr .ib (** kw , metadata = meta )
180+ if metadata is None :
181+ metadata = {}
182+
183+ meta : t .Dict [object , object ] = create_metadata (marshmallow_field )
184+ meta .update (metadata )
185+ new_field : attr ._make ._CountingAttr = attr .ib (** kw , metadata = meta ) # type: ignore[call-overload]
186+ return new_field
88187
89188
90189__version__ = desert ._version .__version__
0 commit comments