SkillAgentSearch skills...

Decorest

Decorator based declarative REST client for Python

Install / Use

/learn @bkryza/Decorest
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

decorest - decorator heavy REST client for Python #################################################

.. image:: https://github.com/bkryza/decorest/actions/workflows/workflow.yml/badge.svg :target: https://github.com/bkryza/decorest/actions/workflows/workflow.yml

.. image:: https://codecov.io/gh/bkryza/decorest/branch/master/graph/badge.svg?token=UGSU07W732 :target: https://codecov.io/gh/bkryza/decorest

.. image:: https://img.shields.io/pypi/v/decorest.svg :target: https://pypi.python.org/pypi/decorest

.. image:: https://img.shields.io/pypi/l/decorest.svg :target: https://pypi.python.org/pypi/decorest

.. image:: https://img.shields.io/pypi/pyversions/decorest.svg :target: https://pypi.python.org/pypi/decorest

Declarative, decorator-based REST client for Python.

.. role:: py(code) :language: python

.. contents::

Overview

decorest_ library provides an easy to use declarative REST API client interface, where definition of the API methods using decorators automatically produces a working REST client with no additional code. In practice the library provides only an interface to describe and interact with REST services - the actual work is done underneath by either requests_ (default) or httpx_ libraries. Backend can be selected dynamically during creation of client instance.

For example:

.. code-block:: python

from decorest import RestClient, GET

class DogClient(RestClient):
    @GET('breed/{breed_name}/list')
    def list_subbreeds(self, breed_name):
        """List all sub-breeds"""

client = DogClient('https://dog.ceo/api')

print(client.list_subbreeds('hound'))

or for an async version (please note the :py:async keyword in the API method definition):

.. code-block:: python

import asyncio
from decorest import backend, RestClient, GET

@backend('httpx')
class DogClient(RestClient):
    @GET('breed/{breed_name}/list')
    async def list_subbreeds(self, breed_name):
        """List all sub-breeds"""

async def main():
    client = DogClient('https://dog.ceo/api')

    print(await client.list_subbreeds('hound'))

asyncio.run(main())

Installation

Note: As of version 0.1.0, decorest supports only Python 3.6+.

Using pip:

.. code-block:: bash

pip install decorest

To install the library with a specific backend, an environment variable must be provided, e.g.:

.. code-block:: bash

# This will only install requests and its dependencies (default)
DECOREST_BACKEND=requests pip install decorest

# This will only install httpx and its dependencies
DECOREST_BACKEND=httpx pip install decorest

Of course both requests_ and httpx_ can be installed together and used exchangeably.

Usage

Basics

For most typical cases the usage should be fairly straightforward. Simply create a subclass of :py:decorest.RestClient and define methods, which will perform calls to the actual REST service. You can declare how each function should perform the request to the service solely using decorators attached to the method definition. The method itself is not expected to have any implementation, except for a docstring.

After your API client class definition is complete, simply create an instance of it and you're good to go. This library relies on the functionality provided by either requests_ or httpx_ libraries, which means that any valid named argument, which could be passed to a requests_ or httpx_ HTTP call can be also passed to the calls of the client methods and will be forwarded as is.

For more information checkout sample clients in examples.

Choosing backend

decorest_ supports currently 2 backends:

  • requests_ (default)
  • httpx_

To select a specific backend, simply pass it's name to the constructor of the client:

.. code-block:: python

client = DogClient('https://dog.ceo/api', backend='httpx')

Another option is to declare a specific default backend for the client using :py:@backend() decorator, for instance:

.. code-block:: python

@decorest.backend('httpx')
class DogClient(decorest.RestClient):
    @GET('breed/{breed_name}/list')
    def list_subbreeds(self, breed_name):
        """List all sub-breeds"""

client = DogClient('https://dog.ceo/api')

If no backend is provided, requests_ is used by default. The client usage is largely independent of the backend, however there some minor differences in handling streams and multipart messages, please consult tests in httpbin test suite_ and httpx compatibility guide_.

Please note, that :py:asyncio is only supported on the httpx_ backend.

Decorators

Below is a list of all supported decorators along with short explanation and examples. Some decorators can be attached to both client class as well as methods, in which case the class-level decorator is applied to all HTTP methods in that class. Furthermore, each decorator can be overridden directly during the method call by providing a named argument with name equal to the decorator name.

@GET, @PUT, @POST, @PATCH, @DELETE, @HEAD, @OPTIONS


Marks the request with a specific HTTP method and the path relative to
endpoint provided as argument. The path can contain variables enclosed
in curly brackets, e.g.:

.. code-block:: python

        @GET('breed/{breed_name}/list')
        def list_subbreeds(self, breed_name):
            """List all sub-breeds"""

which will be replaced by the arguments from the method definition.
These decorators apply only to methods.

@query
~~~~~~

Adds a query parameter to the request. URL encoding will be applied to
the value using :py:`urlencode`, e.g.:

.. code-block:: python

        @GET('breed/{breed_name}/list')
        @query('long_names', 'longNames')
        @query('limit')
        def list_subbreeds(self, breed_name, long_names, limit=100):
            """List all sub-breeds"""

This decorator can take a single string parameter, which determines the name
of the method argument whose value will be added as the query argument value
of the same name.

In case 2 arguments are provided, the second argument determines the actual
query key name, which will be used in the request query (if for some reason
it should be different than the method argument name).

Furthermore, if a default value is provided in a method declaration, it
will be used whenever a value for this argument is not provided during
invocation.

For example, the following invocation of the above method:

.. code-block:: python

    client.list_subbreeds('hound', 1)

will result in the following query:

.. code-block:: bash

    https://dog.ceo/api/breed/hound?longNames=1&limit=100

This decorator can be added only to methods.

@form
~~~~~~

Adds a form parameter to the request. For example:

.. code-block:: python

        @POST('breed')
        @form('breed_name')
        @form('breed_url', 'breed_wikipedia_link')
        def add_breed(self, breed_name, breed_url):
            """Add sub-breed"""

This decorator can take a single string parameter, which determines the name
of the method argument whose value will be added as the query argument value
of the same name.

In case 2 arguments are provided, the second argument determines the actual
form field name, which will be used in the request form (if for some reason
it cannot be the same as the method argument name).

If a method has at least one :py:`@form` decorator attached, the `Content-type`
header value will be always set to `application/x-www-form-urlencoded`.

This decorator can be added only to methods.

@multipart
~~~~~~~~~~

Adds a multipart parameter to the request. For example:

.. code-block:: python

     @POST('post')
     @multipart('part1')
     @multipart('part_2', 'part2')
     @multipart('test')
     def post_multipart(self, part1, part_2, test):
         """Return multipart POST data."""

The first parameter to the decorator is the name of the variable in the decorated
method and at the same time the name of the part in HTTP request (which will be
set in the :py:`Content-Disposition` header. In case the method argument name
should be different than the part name in the request, a second parameter to the 
decorator will determine the actual name for the part in the HTTP request.

The values for the arguments can be either strings, which will be added directly
as content in the appropriate part, or tuples. In case a tuple is passed, it will
be treated as a file, the same way as is treated by both backend libraries. 

The above method can be thus called as follows:

.. code-block:: python

    f = '/tmp/test.dat'
    res = client.post_multipart('TEST1', 'TEST2',
                                ('filename', open(f, 'rb'), 'text/plain'))

which will generate the following parts:
  * part `part1` with content `TEST1`
  * part `part2` with content `TEST2`
  * part `test` with content read from file `/tmp/test.dat`

@header
~~~~~~~

Adds a header key-value pair to the request, e.g.:

.. code-block:: python

        @GET('breed/{breed_name}/list')
        @header('accept', 'application/json')
        def list_subbreeds(self, breed_name):
            """List all sub-breeds"""

This decorator can be added to both methods and client class. The class level
decorators will be added to every method and can be overridden using method
level decorators.

Decorated methods can use their arguments to pass header values, if the headers
name matches one of the arguments, e.g.:

.. code-block:: python

        @GET('breed/{breed_name}/list')
        @header('accept')
        @header('user_agent', 'user-agent')
        def list_subbreeds(self, breed_name, accept, user_agent='decorest'):
            """List all sub-breeds"""

In case the first argument of the header decorator matches one of the
method args, it's optional second value determines the actual header
name that will be se
View on GitHub
GitHub Stars40
CategoryDevelopment
Updated5mo ago
Forks6

Languages

Python

Security Score

87/100

Audited on Oct 31, 2025

No findings