Skip to content

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.

uplink.converters.MarshmallowConverter

MarshmallowConverter()

Bases: Factory

A converter that serializes and deserializes values using marshmallow schemas.

To deserialize JSON responses into Python objects with this converter, define a marshmallow.Schema subclass 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 marshmallow package. For example, here's how to install this feature using pip:

$ pip install uplink[marshmallow]
Source code in uplink/converters/marshmallow_.py
def __init__(self):
    if self.marshmallow is None:
        raise ImportError("No module named '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.

uplink.converters.PydanticConverter

PydanticConverter()

Bases: Factory

A converter that serializes and deserializes values using pydantic.v1 and pydantic models.

To deserialize JSON responses into Python objects with this converter, define a pydantic.v1.BaseModel or pydantic.BaseModel subclass 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 pydantic package. For example, here's how to install this feature using pip:

$ pip install uplink[pydantic]

Validates if :py:mod:pydantic is installed

Source code in uplink/converters/pydantic_.py
def __init__(self):
    """
    Validates if :py:mod:`pydantic` is installed
    """
    if (self.pydantic or self.pydantic_v1) is None:
        raise ImportError("No module named '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:

uplink.converters.TypingConverter

Bases: Factory

Added in v0.5.0

An adapter that serializes and deserializes collection types from the typing module, such as typing.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], where UserSchema is a custom marshmallow.Schema subclass, the converter will resolve the inner type using uplink.converters.MarshmallowConverter.

@get("/users")
def get_users(self) -> typing.Sequence[UserSchema]:
    '''Fetch all users.'''

Note: The typing module 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 typing module by using one of the proxies defined by uplink.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 module-attribute

List = List

A proxy for typing.List that 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"""

uplink.types.Dict module-attribute

Dict = Dict

A proxy for typing.Dict that 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 uplink.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 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):
    ...

uplink.loads

loads(base_class, annotations=())

Bases: _ModelConverterBuilder

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):
    ...

Added in version 0.5.0

PARAMETER DESCRIPTION
base_class

The base model class.

TYPE: type

Source code in uplink/models.py
def __init__(self, base_class, annotations=()):
    """
    Args:
        base_class (type): The base model class.
    """
    self._model_class = base_class
    self._annotations = set(annotations)

from_json classmethod

from_json(base_class, annotations=())

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_json can 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

Added in version 0.5.0

Source code in uplink/models.py
@classmethod
def from_json(cls, base_class, annotations=()):
    """
    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:

    ```python
    @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_json` can leverage
    the registered strategy to deserialize JSON responses.

    For example, the following consumer method would leverage the
    `from_json` strategy defined above:

    ```python
    @returns.from_json
    @get("user")
    def get_user(self) -> User: pass
    ```

    Added in version 0.5.0
    """
    return cls._make_builder(base_class, annotations, returns.json)

uplink.dumps

dumps(base_class, annotations=())

Bases: _ModelConverterBuilder

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):
    ...

Added in version 0.5.0

PARAMETER DESCRIPTION
base_class

The base model class.

TYPE: type

Source code in uplink/models.py
def __init__(self, base_class, annotations=()):
    """
    Args:
        base_class (type): The base model class.
    """
    self._model_class = base_class
    self._annotations = set(annotations)

install class-attribute instance-attribute

install = install

using

using(func)

Sets the converter strategy to the given function.

Source code in uplink/models.py
def using(self, func):
    """Sets the converter strategy to the given function."""
    delegate = _Delegate(self._model_class, self._annotations, func)
    return self._wrap_delegate(delegate)

to_json classmethod

to_json(base_class, annotations=())

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 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, given User is a subclass of ModelBase:

@json
@post("user")
def change_user_name(self, name: Field(type=User): pass

Added in version 0.5.0

Source code in uplink/models.py
@classmethod
def to_json(cls, base_class, annotations=()):
    """
    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.

    ```python
    @dumps.to_json(ModelBase)
    def to_json(model_cls, model_instance):
        return model_instance.to_json()
    ```

    Notably, only consumer methods that are decorated with
    `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, given
    `User` is a subclass of `ModelBase`:

    ```python
    @json
    @post("user")
    def change_user_name(self, name: Field(type=User): pass
    ```

    Added in version 0.5.0
    """
    return cls._make_builder(base_class, annotations, decorators.json)