Source code for uplink.converters.marshmallow_
"""
This module defines a converter that uses :py:mod:`marshmallow` schemas
to deserialize and serialize values.
"""
# Local imports
from uplink import utils
from uplink.converters import interfaces, register_default_converter_factory
[docs]class MarshmallowConverter(interfaces.Factory):
"""
A converter that serializes and deserializes values using
:py:mod:`marshmallow` schemas.
To deserialize JSON responses into Python objects with this
converter, define a :py:class:`marshmallow.Schema` subclass and set
it as the return annotation of a consumer method:
.. code-block:: python
@get("/users")
def get_users(self, username) -> UserSchema():
'''Fetch a single user'''
Note:
This converter is an optional feature and requires the
:py:mod:`marshmallow` package. For example, here's how to
install this feature using pip::
$ pip install uplink[marshmallow]
"""
try:
import marshmallow
except ImportError: # pragma: no cover
marshmallow = None
is_marshmallow_3 = None
else:
is_marshmallow_3 = marshmallow.__version__ >= "3.0"
def __init__(self):
if self.marshmallow is None:
raise ImportError("No module named 'marshmallow'")
class ResponseBodyConverter(interfaces.Converter):
def __init__(self, extract_data, schema):
self._extract_data = extract_data
self._schema = schema
def convert(self, response):
try:
json = response.json()
except AttributeError:
# Assume that the response is already json
json = response
return self._extract_data(self._schema.load(json))
class RequestBodyConverter(interfaces.Converter):
def __init__(self, extract_data, schema):
self._extract_data = extract_data
self._schema = schema
def convert(self, value):
return self._extract_data(self._schema.dump(value))
@classmethod
def _get_schema(cls, type_):
if utils.is_subclass(type_, cls.marshmallow.Schema):
return type_()
elif isinstance(type_, cls.marshmallow.Schema):
return type_
raise ValueError("Expected marshmallow.Scheme subclass or instance.")
def _extract_data(self, m):
# After marshmallow 3.0, Schema.load() and Schema.dump() don't
# return a (data, errors) tuple any more. Only `data` is returned.
return m if self.is_marshmallow_3 else m.data
def _make_converter(self, converter_cls, type_):
try:
# Try to generate schema instance from the given type.
schema = self._get_schema(type_)
except ValueError:
# Failure: the given type is not a `marshmallow.Schema`.
return None
else:
return converter_cls(self._extract_data, schema)
def create_request_body_converter(self, type_, *args, **kwargs):
return self._make_converter(self.RequestBodyConverter, type_)
def create_response_body_converter(self, type_, *args, **kwargs):
return self._make_converter(self.ResponseBodyConverter, type_)
@classmethod
def register_if_necessary(cls, register_func):
if cls.marshmallow is not None:
register_func(cls)
MarshmallowConverter.register_if_necessary(register_default_converter_factory)