Converters¶
The converter parameter of the uplink.Consumer
constructor accepts a custom adapter class that handles serialization of
HTTP request properties and deserialization of HTTP response objects:
github = GitHub(BASE_URL, converter=...)
Starting with version v0.5, some out-of-the-box converters are included
automatically and don’t need to be explicitly provided through the
converter parameter. These implementations are detailed below.
Marshmallow¶
Uplink comes with optional support for marshmallow.
-
class
uplink.converters.MarshmallowConverter[source]¶ A converter that serializes and deserializes values using
marshmallowschemas.To deserialize JSON responses into Python objects with this converter, define a
marshmallow.Schemasubclass and set it as the return annotation of a consumer method:@get("/users") def get_users(self, username) -> UserSchema(): '''Fetch a single user'''
Note
This converter is an optional feature and requires the
marshmallowpackage. For example, here’s how to install this feature using pip:$ pip install uplink[marshmallow]
Note
Starting with version v0.5, this converter factory is automatically
included if you have marshmallow installed, so you don’t need
to provide it when constructing your consumer instances.
Pydantic¶
New in version v0.9.2.
Uplink comes with optional support for pydantic.
-
class
uplink.converters.PydanticConverter[source]¶ A converter that serializes and deserializes values using
pydanticmodels.To deserialize JSON responses into Python objects with this converter, define a
pydantic.BaseModelsubclass and set it as the return annotation of a consumer method:@returns.json() @get("/users") def get_users(self, username) -> List[UserModel]: '''Fetch multiple users'''
Note
This converter is an optional feature and requires the
pydanticpackage. For example, here’s how to install this feature using pip:$ pip install uplink[pydantic]
Note
Starting with version v0.9.2, this converter factory is automatically
included if you have pydantic installed, so you don’t need
to provide it when constructing your consumer instances.
Converting Collections¶
New in version v0.5.0.
Uplink can convert collections of a type, such as deserializing a
response body into a list of users. If you have typing
installed (the module is part of the standard library starting Python
3.5), you can use type hints (see PEP 484) to specify such
conversions. You can also leverage this feature without typing
by using one of the proxy types defined in uplink.types.
The following converter factory implements this feature and is automatically included, so you don’t need to provide it when constructing your consumer instance:
-
class
uplink.converters.TypingConverter[source]¶ An adapter that serializes and deserializes collection types from the
typingmodule, such astyping.List.Inner types of a collection are recursively resolved, using other available converters if necessary. For instance, when resolving the type hint
typing.Sequence[UserSchema], whereUserSchemais a custommarshmallow.Schemasubclass, the converter will resolve the inner type usinguplink.converters.MarshmallowConverter.@get("/users") def get_users(self) -> typing.Sequence[UserSchema]: '''Fetch all users.'''
Note
The
typingmodule is available in the standard library starting from Python 3.5. For earlier versions of Python, there is a port of the module available on PyPI.However, you can utilize this converter without the
typingmodule by using one of the proxies defined byuplink.returns(e.g.,uplink.types.List).
Here are the collection types defined in uplink.types. You can
use these or the corresponding type hints from typing to leverage
this feature:
-
uplink.types.List¶ A proxy for
typing.Listthat is safe to use in type hints with Python 3.4 and below.@get("/users") def get_users(self) -> types.List[str]: """Fetches all users"""
A proxy for
typing.Listthat is safe to use in type hints with Python 3.4 and below.@returns.from_json @get("/users") def get_users(self) -> types.List[str]: """Fetches all users"""
-
uplink.types.Dict¶ A proxy for
typing.Dictthat is safe to use in type hints with Python 3.4 and below.@returns.from_json @get("/users") def get_users(self) -> types.Dict[str, str]: """Fetches all users"""
A proxy for
typing.Dictthat is safe to use in type hints with Python 3.4 and below.@returns.from_json @get("/users") def get_users(self) -> types.Dict[str, str]: """Fetches all users"""
Writing Custom JSON Converters¶
As a shorthand, you can define custom JSON converters using the
@loads.from_json (deserialization) and
@dumps.to_json (serialization) decorators.
These classes can be used as decorators to create converters of a class and its subclasses:
# Creates a converter that can deserialize the given `json` in to an
# instance of a `Model` subtype.
@loads.from_json(Model)
def load_model_from_json(model_type, json):
...
Note
Unlike consumer methods, these functions should be defined outside of a class scope.
To use the converter, provide the generated converter object when
instantiating a Consumer subclass, through the
converter constructor parameter:
github = GitHub(BASE_URL, converter=load_model_from_json)
Alternatively, you can add the @install decorator to
register the converter function as a default converter, meaning the
converter will be included automatically with any consumer instance and
doesn’t need to be explicitly provided through the :py:obj:converter
parameter:
from uplink import install, loads
# Register the function as a default loader for the given model class.
@install
@loads.from_json(Model)
def load_model_from_json(model_type, json):
...
-
class
uplink.loads(base_class, annotations=())[source]¶ Builds a custom object deserializer.
This class takes a single argument, the base model class, and registers the decorated function as a deserializer for that base class and all subclasses.
Further, the decorated function should accept two positional arguments: (1) the encountered type (which can be the given base class or a subclass), and (2) the response data.
@loads(ModelBase) def load_model(model_cls, data): ...
New in version v0.5.0.
-
classmethod
from_json(base_class, annotations=())[source]¶ Builds a custom JSON deserialization strategy.
This decorator accepts the same arguments and behaves like
uplink.loads, except that the second argument of the decorated function is a JSON object:@loads.from_json(User) def from_json(user_cls, json): return user_cls(json["id"], json["username"])
Notably, only consumer methods that have the expected return type (i.e., the given base class or any subclass) and are decorated with
uplink.returns.from_jsoncan leverage the registered strategy to deserialize JSON responses.For example, the following consumer method would leverage the
from_json()strategy defined above:@returns.from_json @get("user") def get_user(self) -> User: pass
New in version v0.5.0.
-
classmethod
-
class
uplink.dumps(base_class, annotations=())[source]¶ Builds a custom object serializer.
This decorator takes a single argument, the base model class, and registers the decorated function as a serializer for that base class and all subclasses.
Further, the decorated function should accept two positional arguments: (1) the encountered type (which can be the given base class or a subclass), and (2) the encountered instance.
@dumps(ModelBase) def deserialize_model(model_cls, model_instance): ...
New in version v0.5.0.
-
classmethod
to_json(base_class, annotations=())[source]¶ Builds a custom JSON serialization strategy.
This decorator accepts the same arguments and behaves like
uplink.dumps. The only distinction is that the decorated function should be JSON serializable.@dumps.to_json(ModelBase) def to_json(model_cls, model_instance): return model_instance.to_json()
Notably, only consumer methods that are decorated with py:class:uplink.json and have one or more argument annotations with the expected type (i.e., the given base class or a subclass) can leverage the registered strategy.
For example, the following consumer method would leverage the
to_json()strategy defined above, givenUseris a subclass ofModelBase:@json @post("user") def change_user_name(self, name: Field(type=User): pass
New in version v0.5.0.
-
classmethod