Skip to content

Commit

Permalink
docs(pydantic): Add docstrings for pydantic's converter and examples …
Browse files Browse the repository at this point in the history
…on how to use it
  • Loading branch information
gmcrocetti committed Sep 10, 2020
1 parent a933f1f commit 07edb49
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 7 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ Contributors
- Alexander Duryagin (`@daa <https://github.com/daa>`_)
- Sakorn Waungwiwatsin (`@SakornW <https://github.com/SakornW>`_)
- Jacob Floyd (`@cognifloyd <https://github.com/cognifloyd>`_)
- Guilherme Crocetti (`@gmcrocetti <https://github.com/gmcrocetti/>`_)
7 changes: 4 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Features

- Define `custom converters`_ for your own objects
- Support for |marshmallow|_ schemas and `handling collections`_ (e.g., list of Users)
- Support for pydantic models and :ref:`handling collections <converting_collections>` (e.g., list of Repos)

- **Extendable**

Expand Down Expand Up @@ -114,7 +115,7 @@ If you are interested in the cutting-edge, preview the upcoming release with:
Extra! Extra!
-------------

Further, uplink has optional integrations and features. You can view a full list
Further, uplink has optional integrations and features. You can view a full list
of available extras `here <http://uplink.readthedocs.io/en/latest/install.html#extras>`_.

When installing Uplink with ``pip``, you can select extras using the format:
Expand Down Expand Up @@ -188,8 +189,8 @@ Thank you for taking the time to improve an open source project :purple_heart:
.. |Code Style| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/ambv/black
:alt: Code style: black
.. |Coverage Status| image:: https://img.shields.io/codecov/c/github/prkumar/uplink.svg
:alt: Codecov
.. |Coverage Status| image:: https://img.shields.io/codecov/c/github/prkumar/uplink.svg
:alt: Codecov
:target: https://codecov.io/gh/prkumar/uplink
.. |Documentation Status| image:: https://readthedocs.org/projects/uplink/badge/?version=latest
:target: http://uplink.readthedocs.io/en/latest/?badge=latest
Expand Down
15 changes: 15 additions & 0 deletions docs/source/dev/converters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ Uplink comes with optional support for :py:mod:`marshmallow`.
included if you have :py:mod:`marshmallow` installed, so you don't need
to provide it when constructing your consumer instances.

Pydantic
===========

.. versionadded:: v0.9.2

Uplink comes with optional support for :py:mod:`pydantic`.

.. autoclass:: uplink.converters.PydanticConverter

.. note::

Starting with version v0.9.2, this converter factory is automatically
included if you have :py:mod:`pydantic` installed, so you don't need
to provide it when constructing your consumer instances.

.. _`converting lists and mappings`:

Converting Collections
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Features

- Define :ref:`custom converters <custom_json_deserialization>` for your own objects
- Support for |marshmallow|_ schemas and :ref:`handling collections <converting_collections>` (e.g., list of Users)
- Support for pydantic models and :ref:`handling collections <converting_collections>` (e.g., list of Repos)

- **Extendable**

Expand Down
5 changes: 4 additions & 1 deletion docs/source/user/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ Extra Description
for `converting JSON responses directly into Python objects
<https://github.com/prkumar/uplink/tree/master/examples/marshmallow>`_
using :py:class:`marshmallow.Schema`.
``pydantic`` Enables :py:class:`uplink.PydanticConverter`,
for converting JSON responses directly into Python objects
using :py:class:`pydantic.BaseModel`.
``twisted`` Enables :py:class:`uplink.TwistedClient`,
for `sending non-blocking requests <https://github.com/prkumar/uplink/tree/master/examples/async-requests>`_ and receiving
:py:class:`~twisted.internet.defer.Deferred` responses.
Expand All @@ -62,5 +65,5 @@ To download all available features, run

::

$ pip install -U uplink[aiohttp, marshmallow, twisted]
$ pip install -U uplink[aiohttp, marshmallow, pydantic, twisted]

88 changes: 87 additions & 1 deletion docs/source/user/serialization.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dealing with the underlying protocol.

This document walks you through how to leverage Uplink's serialization support,
including integrations for third-party serialization libraries like
:mod:`marshmallow` and tools for writing custom conversion strategies that
:mod:`marshmallow`, :mod:`pydantic` and tools for writing custom conversion strategies that
fit your unique needs.

.. _using_marshmallow_schemas:
Expand Down Expand Up @@ -79,6 +79,71 @@ schema:
For a more complete example of Uplink's :mod:`marshmallow` support,
check out `this example on GitHub <https://github.com/prkumar/uplink/tree/master/examples/marshmallow>`_.

.. _using_pydantic_schemas:

Using Pydantic Models
=========================

:mod:`pydantic` is a framework-agnostic, object serialization library
for Python >= 3.6. Uplink comes with built-in support for Pydantic; you can
integrate your Pydantic models with Uplink for easy JSON (de)serialization.

First, create a :class:`pydantic.BaseModel`, declaring any necessary
conversions and validations. Here's a simple example:

.. code-block:: python
from typing import List
from pydantic import BaseModel, HttpUrl
class Owner(BaseModel):
id: int
avatar_url: HttpUrl
organizations_url: HttpUrl
class Repo(BaseModel):
id: int
full_name: str
owner: Owner
Then, specify the schema using the :class:`@returns <uplink.returns>` decorator:

.. code-block:: python
:emphasize-lines: 2
class GitHub(Consumer):
@returns.json(List[Repo])
@get("users/{username}/repos")
def get_repos(self, username):
"""Get the user's public repositories."""
Python 3 users can use a return type hint instead:

.. code-block:: python
:emphasize-lines: 3
class GitHub(Consumer):
@returns.json()
@get("users/{username}/repos")
def get_repos(self, username) -> List[Repo]:
"""Get the user's public repositories."""
Your consumer should now return Python objects based on your Pydantic
model:

.. code-block:: python
github = GitHub(base_url="https://api.github.com")
print(github.get_repos("octocat"))
# Output: [User(id=132935648, full_name='octocat/boysenberry-repo-1', owner=Owner(...), ...]
.. note::

You may have noticed the usage of `returns.json` in both examples. Unlike :mod:`marshmallow`, :mod:`pydantic`
has no `many` parameter to control the deserialization of multiple objects. The recommended approach
is to use `returns.json` instead of defining a new model with a `__root__` element.

Serializing Method Arguments
============================

Expand Down Expand Up @@ -111,6 +176,27 @@ Uplink's :mod:`marshmallow` integration (see
repo = Repo(name="my_favorite_new_project")
github.create_repo(repo)
The sample code above using :mod:`marshmallow` is also reproducible using :mod:`pydantic`:

.. code-block:: python
from uplink import Consumer, Body
class CreateRepo(BaseModel):
name: str
delete_branch_on_merge: bool
class GitHub(Consumer):
@post("user/repos")
def create_repo(self, repo: Body(type=CreateRepo)):
"""Creates a new repository for the authenticated user."""
Then, calling the client.

.. code-block:: python
repo = CreateRepo(name="my-new-uplink-pydantic", delete_branch_on_merge=True)
github.create_repo(repo)
.. _custom_json_deserialization:

Custom JSON Conversion
Expand Down
13 changes: 11 additions & 2 deletions uplink/converters/pydantic_.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
"""
This module defines a converter that uses :py:mod:`pydantic` models
to deserialize and serialize values.
"""

from uplink.converters import register_default_converter_factory
from uplink.converters.interfaces import Factory, Converter
from uplink.utils import is_subclass
Expand Down Expand Up @@ -35,9 +40,10 @@ class PydanticConverter(Factory):
.. code-block:: python
@returns.json()
@get("/users")
def get_users(self, username) -> UserModel():
'''Fetch a single user'''
def get_users(self, username) -> List[UserModel]:
'''Fetch multiple users'''
Note:
Expand All @@ -54,6 +60,9 @@ def get_users(self, username) -> UserModel():
pydantic = None

def __init__(self):
"""
Validates if :py:mod:`pydantic` is installed
"""
if self.pydantic is None:
raise ImportError("No module named 'pydantic'")

Expand Down

0 comments on commit 07edb49

Please sign in to comment.