Skip to content

Commit 2a5b607

Browse files
authored
Merge pull request #804 from marshmallow-code/map_to_openapi_type
map_to_openapi_type: change from decorator to classic function
2 parents c084e1c + 00b0b96 commit 2a5b607

File tree

5 files changed

+46
-29
lines changed

5 files changed

+46
-29
lines changed

docs/using_plugins.rst

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ custom field subclasses a standard marshmallow `Field` class then it will
243243
inherit the default mapping. If you want to override the OpenAPI type and format
244244
for custom fields, use the
245245
`map_to_openapi_type <apispec.ext.marshmallow.MarshmallowPlugin.map_to_openapi_type>`
246-
decorator. It can be invoked with either a pair of strings providing the
246+
method. It can be invoked with either a pair of strings providing the
247247
OpenAPI type and format, or a marshmallow `Field` that has the desired target mapping.
248248

249249
.. code-block:: python
@@ -258,21 +258,26 @@ OpenAPI type and format, or a marshmallow `Field` that has the desired target ma
258258
title="Demo", version="0.1", openapi_version="3.0.0", plugins=(ma_plugin,)
259259
)
260260
261-
# Inherits Integer mapping of ('integer', 'int32')
262-
class MyCustomInteger(Integer):
261+
# Inherits Integer mapping of ('integer', None)
262+
class CustomInteger(Integer):
263263
pass
264264
265265
266266
# Override Integer mapping
267-
@ma_plugin.map_to_openapi_type("string", "uuid")
268-
class MyCustomField(Integer):
267+
class Int32(Integer):
269268
pass
270269
271270
272-
@ma_plugin.map_to_openapi_type(Integer) # will map to ('integer', 'int32')
273-
class MyCustomFieldThatsKindaLikeAnInteger(Field):
271+
ma_plugin.map_to_openapi_type(Int32, "string", "int32")
272+
273+
274+
# Map to ('integer', None) like Integer
275+
class IntegerLike(Field):
274276
pass
275277
278+
279+
ma_plugin.map_to_openapi_type(IntegerLike, Integer)
280+
276281
In situations where greater control of the properties generated for a custom field
277282
is desired, users may add custom logic to the conversion of fields to OpenAPI properties
278283
through the use of the `add_attribute_function

src/apispec/ext/marshmallow/__init__.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,10 @@ def init_spec(self, spec: APISpec) -> None:
137137
openapi_version=spec.openapi_version, converter=self.converter
138138
)
139139

140-
def map_to_openapi_type(self, *args):
141-
"""Decorator to set mapping for custom fields.
140+
def map_to_openapi_type(self, field_cls, *args):
141+
"""Set mapping for custom field class.
142+
143+
:param type field_cls: Field class to set mapping for.
142144
143145
``*args`` can be:
144146
@@ -147,15 +149,19 @@ def map_to_openapi_type(self, *args):
147149
148150
Examples: ::
149151
150-
@ma_plugin.map_to_openapi_type('string', 'uuid')
151-
class MyCustomField(Integer):
152+
# Override Integer mapping
153+
class Int32(Integer):
152154
# ...
153155
154-
@ma_plugin.map_to_openapi_type(Integer) # will map to ('integer', None)
155-
class MyCustomFieldThatsKindaLikeAnInteger(Integer):
156+
ma_plugin.map_to_openapi_type(Int32, 'string', 'int32')
157+
158+
# Map to ('integer', None) like Integer
159+
class IntegerLike(Integer):
156160
# ...
161+
162+
ma_plugin.map_to_openapi_type(IntegerLike, Integer)
157163
"""
158-
return self.converter.map_to_openapi_type(*args)
164+
return self.converter.map_to_openapi_type(field_cls, *args)
159165

160166
def schema_helper(self, name, _, schema=None, **kwargs):
161167
"""Definition helper that allows using a marshmallow

src/apispec/ext/marshmallow/field_converter.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ def init_attribute_functions(self):
113113
self.timedelta2properties,
114114
]
115115

116-
def map_to_openapi_type(self, *args):
117-
"""Decorator to set mapping for custom fields.
116+
def map_to_openapi_type(self, field_cls, *args):
117+
"""Set mapping for custom field class.
118+
119+
:param type field_cls: Field class to set mapping for.
118120
119121
``*args`` can be:
120122
@@ -128,11 +130,7 @@ def map_to_openapi_type(self, *args):
128130
else:
129131
raise TypeError("Pass core marshmallow field type or (type, fmt) pair.")
130132

131-
def inner(field_type):
132-
self.field_mapping[field_type] = openapi_type_field
133-
return field_type
134-
135-
return inner
133+
self.field_mapping[field_cls] = openapi_type_field
136134

137135
def add_attribute_function(self, func):
138136
"""Method to add an attribute function to the list of attribute functions

tests/test_ext_marshmallow.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,19 +314,25 @@ def test_can_use_schema_in_header(self, spec, schema):
314314

315315
class TestCustomField:
316316
def test_can_use_custom_field_decorator(self, spec_fixture):
317-
@spec_fixture.marshmallow_plugin.map_to_openapi_type(DateTime)
318317
class CustomNameA(Field):
319318
pass
320319

321-
@spec_fixture.marshmallow_plugin.map_to_openapi_type("integer", "int32")
320+
spec_fixture.marshmallow_plugin.map_to_openapi_type(CustomNameA, DateTime)
321+
322322
class CustomNameB(Field):
323323
pass
324324

325-
with pytest.raises(TypeError):
325+
spec_fixture.marshmallow_plugin.map_to_openapi_type(
326+
CustomNameB, "integer", "int32"
327+
)
326328

327-
@spec_fixture.marshmallow_plugin.map_to_openapi_type("integer")
328-
class BadCustomField(Field):
329-
pass
329+
class BadCustomField(Field):
330+
pass
331+
332+
with pytest.raises(TypeError):
333+
spec_fixture.marshmallow_plugin.map_to_openapi_type(
334+
BadCustomField, "integer"
335+
)
330336

331337
class CustomPetASchema(PetSchema):
332338
name = CustomNameA()

tests/test_ext_marshmallow_field.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,10 +212,11 @@ def test_field_with_range_string_type(spec_fixture, field):
212212

213213
@pytest.mark.parametrize("spec_fixture", ("3.1.0",), indirect=True)
214214
def test_field_with_range_type_list_with_number(spec_fixture):
215-
@spec_fixture.openapi.map_to_openapi_type(["integer", "null"], None)
216215
class NullableInteger(fields.Field):
217216
"""Nullable integer"""
218217

218+
spec_fixture.openapi.map_to_openapi_type(NullableInteger, ["integer", "null"], None)
219+
219220
field = NullableInteger(validate=validate.Range(min=1, max=10))
220221
res = spec_fixture.openapi.field2property(field)
221222
assert res["minimum"] == 1
@@ -225,10 +226,11 @@ class NullableInteger(fields.Field):
225226

226227
@pytest.mark.parametrize("spec_fixture", ("3.1.0",), indirect=True)
227228
def test_field_with_range_type_list_without_number(spec_fixture):
228-
@spec_fixture.openapi.map_to_openapi_type(["string", "null"], None)
229229
class NullableInteger(fields.Field):
230230
"""Nullable integer"""
231231

232+
spec_fixture.openapi.map_to_openapi_type(NullableInteger, ["string", "null"], None)
233+
232234
field = NullableInteger(validate=validate.Range(min=1, max=10))
233235
res = spec_fixture.openapi.field2property(field)
234236
assert res["x-minimum"] == 1

0 commit comments

Comments
 (0)