Skip to content

Decorators

The method decorators detailed in this section describe request properties that are relevant to all invocations of a consumer method.

uplink.headers

headers(arg=None, **kwargs)

A decorator that adds static headers for API calls.

@headers({"User-Agent": "Uplink-Sample-App"})
@get("/user")
def get_user(self):
    """Get the current user"""

When used as a class decorator, headers applies to all consumer methods bound to the class:

@headers({"Accept": "application/vnd.github.v3.full+json"})
class GitHub(Consumer):
    ...

headers takes the same arguments as dict.

PARAMETER DESCRIPTION
arg

A dict containing header values.

DEFAULT: None

**kwargs

More header values.

DEFAULT: {}

Source code in uplink/decorators.py
def __init__(self, arg=None, **kwargs):
    if isinstance(arg, str):
        key, value = self._split(arg)
        arg = {key: value}
    super().__init__(arg or {}, **kwargs)

uplink.params

params(arg=None, **kwargs)

A decorator that adds static query parameters for API calls.

@params({"sort": "created"})
@get("/user")
def get_user(self):
    """Get the current user"""

When used as a class decorator, params applies to all consumer methods bound to the class:

@params({"client_id": "my-app-client-id"})
class GitHub(Consumer):
    ...

params takes the same arguments as dict.

PARAMETER DESCRIPTION
arg

A dict containing query parameters.

DEFAULT: None

**kwargs

More query parameters.

DEFAULT: {}

Source code in uplink/decorators.py
def __init__(self, arg=None, **kwargs):
    if isinstance(arg, str):
        arg = arg.split("&")
    super().__init__(arg or {}, **kwargs)

uplink.json

Use as a decorator to make JSON requests.

You can annotate a method argument with uplink.Body, which indicates that the argument's value should become the request's body. uplink.Body has to be either a dict or a subclass of abc.Mapping.

Example
@json
@patch(/user")
def update_user(self, **info: Body):
    """Update the current user."""

You can alternatively use the uplink.Field annotation to specify JSON fields separately, across multiple arguments:

Example
@json
@patch(/user")
def update_user(self, name: Field, email: Field("e-mail")):
    """Update the current user."""

Further, to set a nested field, you can specify the path of the target field with a tuple of strings as the first argument of uplink.Field.

Example

Consider a consumer method that sends a PATCH request with a JSON body of the following format:

{
    user: {
        name: "<User's Name>"
    },
}

The tuple ("user", "name") specifies the path to the highlighted inner field:

@json
@patch(/user")
def update_user(
                self,
                new_name: Field(("user", "name"))
):
    """Update the current user."""

uplink.form_url_encoded

URL-encodes the request body.

Used on POST/PUT/PATCH request. It url-encodes the body of the message and sets the appropriate Content-Type header. Further, each field argument should be annotated with uplink.Field.

Example
@form_url_encoded
@post("/users/edit")
def update_user(self, first_name: Field, last_name: Field):
    """Update the current user."""

uplink.multipart

Sends multipart form data.

Multipart requests are commonly used to upload files to a server. Further, annotate each part argument with Part.

Examples:

@multipart
@put("/user/photo")
def update_user(self, photo: Part, description: Part):
    """Upload a user profile photo."""

uplink.timeout

timeout(seconds)

Time to wait for a server response before giving up.

When used on other decorators it specifies how long (in seconds) a decorator should wait before giving up.

Example
@timeout(60)
@get("/user/posts")
def get_posts(self):
    """Fetch all posts for the current users."""

When used as a class decorator, timeout applies to all consumer methods bound to the class.

PARAMETER DESCRIPTION
seconds

An integer used to indicate how long should the request wait.

Source code in uplink/decorators.py
def __init__(self, seconds):
    self._seconds = seconds

uplink.args

args(*annotations, **more_annotations)

Annotate method arguments using positional or keyword arguments.

Arrange annotations in the same order as their corresponding function arguments.

Examples:

Basic usage with positional annotations:

@args(Path, Query)
@get("/users/{username}")
def get_user(self, username, visibility):
    """Get a specific user."""

Using keyword args to target specific method parameters:

@args(visibility=Query)
@get("/users/{username}")
def get_user(self, username, visibility):
    """Get a specific user."""
PARAMETER DESCRIPTION
*annotations

Any number of annotations.

DEFAULT: ()

**more_annotations

More annotations, targeting specific method arguments.

DEFAULT: {}

Source code in uplink/decorators.py
def __init__(self, *annotations, **more_annotations):
    self._annotations = annotations
    self._more_annotations = more_annotations

uplink.response_handler

response_handler(handler, requires_consumer=False)

A decorator for creating custom response handlers.

To register a function as a custom response handler, decorate the function with this class. The decorated function should accept a single positional argument, an HTTP response object:

Example:

@response_handler
def raise_for_status(response):
    response.raise_for_status()
    return response

Then, to apply custom response handling to a request method, simply decorate the method with the registered response handler:

Example:

@raise_for_status
@get("/user/posts")
def get_posts(self):
    """Fetch all posts for the current users."""

To apply custom response handling on all request methods of a uplink.Consumer subclass, simply decorate the class with the registered response handler:

Example:

@raise_for_status
class GitHub(Consumer):
   ...

Lastly, the decorator supports the optional argument requires_consumer. When this option is set to True, the registered callback should accept a reference to the Consumer instance as its leading argument:

Example:

@response_handler(requires_consumer=True)
def raise_for_status(consumer, response):
    ...

Added in version 0.4.0

Source code in uplink/hooks.py
def __init__(self, handler, requires_consumer=False):
    self.handle_response = _wrap_if_necessary(handler, requires_consumer)

uplink.error_handler

error_handler(exception_handler, requires_consumer=False)

A decorator for creating custom error handlers.

To register a function as a custom error handler, decorate the function with this class. The decorated function should accept three positional arguments: (1) the type of the exception, (2) the exception instance raised, and (3) a traceback instance.

Example:

@error_handler
def raise_api_error(exc_type, exc_val, exc_tb):
    # wrap client error with custom API error
    ...

Then, to apply custom error handling to a request method, simply decorate the method with the registered error handler:

Example:

@raise_api_error
@get("/user/posts")
def get_posts(self):
    """Fetch all posts for the current users."""

To apply custom error handling on all request methods of a uplink.Consumer subclass, simply decorate the class with the registered error handler:

Example:

@raise_api_error
class GitHub(Consumer):
   ...

Lastly, the decorator supports the optional argument requires_consumer. When this option is set to True, the registered callback should accept a reference to the Consumer instance as its leading argument:

Example:

@error_handler(requires_consumer=True)
def raise_api_error(consumer, exc_type, exc_val, exc_tb):
    ...

Added in version 0.4.0

Note

Error handlers can not completely suppress exceptions. The original exception is thrown if the error handler doesn't throw anything.

Source code in uplink/hooks.py
def __init__(self, exception_handler, requires_consumer=False):
    self.handle_exception = _wrap_if_necessary(exception_handler, requires_consumer)

uplink.inject

inject(*hooks)

A decorator that applies one or more hooks to a request method.

Added in version 0.4.0

Source code in uplink/hooks.py
def __init__(self, *hooks):
    self._hooks = hooks
    self._response_handlers = []

    # TODO: If more than one callback exists on the chain, the chain
    # expects it can execute each synchronously. Instead, we should
    # be smart about this and produces a chained coroutine when all
    # callbacks are coroutines, so that the client can execute the
    # chain asynchronously. Further, when provided both synchronous
    # and asynchronous callbacks, we should raise an exception when
    # the order is mixed and split into two chains (one async and
    # the other sync) when the order permits separation.

    # Adding a synchronous callback to an async request forces the
    # request to execute synchronously while running this chain. To
    # avoid unnecessarily executing this chain when no callbacks
    # exists, we can set the `handle_response` method to null,
    # indicating that this hook doesn't handle responses.
    response_handlers = [h for h in hooks if h.handle_response is not None]
    if not response_handlers:
        self.handle_response = None
    elif len(response_handlers) == 1:
        self.handle_response = response_handlers[0].handle_response

    self._response_handlers = response_handlers

HTTP Method Decorators

These decorators define the HTTP method to use for a request.

uplink.get

get(uri=None, args=())

Creates a decorator that makes the decorated function an HTTP GET request handler.

PARAMETER DESCRIPTION
uri

The URI template for the request. Can include path parameters in the format {param_name}. If not provided, can be specified when the decorator is used.

TYPE: str DEFAULT: None

args

A sequence of argument annotations or mapping of argument names to annotations for the handler method.

TYPE: tuple | dict DEFAULT: ()

RETURNS DESCRIPTION

A decorator that can be used to define a GET request handler method.

Examples:

Basic usage with path parameter:

@get("users/{username}")
def get_user(self, username):
    """Get a specific user."""

Using query parameters:

@get("users")
def list_users(self, page: Query, per_page: Query = 30):
    """List users with pagination."""
Source code in uplink/commands.py
def get(uri=None, args=()):
    """Creates a decorator that makes the decorated function an HTTP GET request handler.

    Args:
        uri (str, optional): The URI template for the request. Can include path parameters
            in the format {param_name}. If not provided, can be specified when the decorator is used.
        args (tuple|dict, optional): A sequence of argument annotations or mapping of
            argument names to annotations for the handler method.

    Returns:
        A decorator that can be used to define a GET request handler method.

    Examples:
        Basic usage with path parameter:

        ```python
        @get("users/{username}")
        def get_user(self, username):
            \"""Get a specific user.\"""
        ```

        Using query parameters:

        ```python
        @get("users")
        def list_users(self, page: Query, per_page: Query = 30):
            \"""List users with pagination.\"""
        ```
    """
    return HttpMethodFactory("GET").__call__(uri, args)

uplink.post

post(uri=None, args=())

Creates a decorator that makes the decorated function an HTTP POST request handler.

PARAMETER DESCRIPTION
uri

The URI template for the request. Can include path parameters in the format {param_name}. If not provided, can be specified when the decorator is used.

TYPE: str DEFAULT: None

args

A sequence of argument annotations or mapping of argument names to annotations for the handler method.

TYPE: tuple | dict DEFAULT: ()

RETURNS DESCRIPTION

A decorator that can be used to define a POST request handler method.

Examples:

Using JSON body:

@json
@post("users")
def create_user(self, **user_info: Body):
    """Create a new user."""

Using form data:

@form_url_encoded
@post("auth/login")
def login(self, username: Field, password: Field):
    """Login with credentials."""
Source code in uplink/commands.py
def post(uri=None, args=()):
    """Creates a decorator that makes the decorated function an HTTP POST request handler.

    Args:
        uri (str, optional): The URI template for the request. Can include path parameters
            in the format {param_name}. If not provided, can be specified when the decorator is used.
        args (tuple|dict, optional): A sequence of argument annotations or mapping of
            argument names to annotations for the handler method.

    Returns:
        A decorator that can be used to define a POST request handler method.

    Examples:
        Using JSON body:

        ```python
        @json
        @post("users")
        def create_user(self, **user_info: Body):
            \"""Create a new user.\"""
        ```

        Using form data:

        ```python
        @form_url_encoded
        @post("auth/login")
        def login(self, username: Field, password: Field):
            \"""Login with credentials.\"""
        ```
    """
    return HttpMethodFactory("POST")(uri, args)

uplink.put

put(uri=None, args=())

Creates a decorator that makes the decorated function an HTTP PUT request handler.

PARAMETER DESCRIPTION
uri

The URI template for the request. Can include path parameters in the format {param_name}. If not provided, can be specified when the decorator is used.

TYPE: str DEFAULT: None

args

A sequence of argument annotations or mapping of argument names to annotations for the handler method.

TYPE: tuple | dict DEFAULT: ()

RETURNS DESCRIPTION

A decorator that can be used to define a PUT request handler method.

Examples:

Using request body:

@put("users/{username}")
def update_user(self, username, **user_info: Body):
    """Update a specific user."""

Using form fields:

@form_url_encoded
@put("users/{username}")
def update_user(self, username, name: Field, email: Field):
    """Update a user's information."""
Source code in uplink/commands.py
def put(uri=None, args=()):
    """Creates a decorator that makes the decorated function an HTTP PUT request handler.

    Args:
        uri (str, optional): The URI template for the request. Can include path parameters
            in the format {param_name}. If not provided, can be specified when the decorator is used.
        args (tuple|dict, optional): A sequence of argument annotations or mapping of
            argument names to annotations for the handler method.

    Returns:
        A decorator that can be used to define a PUT request handler method.

    Examples:
        Using request body:

        ```python
        @put("users/{username}")
        def update_user(self, username, **user_info: Body):
            \"""Update a specific user.\"""
        ```

        Using form fields:

        ```python
        @form_url_encoded
        @put("users/{username}")
        def update_user(self, username, name: Field, email: Field):
            \"""Update a user's information.\"""
        ```
    """
    return HttpMethodFactory("PUT")(uri, args)

uplink.patch

patch(uri=None, args=())

Creates a decorator that makes the decorated function an HTTP PATCH request handler.

PARAMETER DESCRIPTION
uri

The URI template for the request. Can include path parameters in the format {param_name}. If not provided, can be specified when the decorator is used.

TYPE: str DEFAULT: None

args

A sequence of argument annotations or mapping of argument names to annotations for the handler method.

TYPE: tuple | dict DEFAULT: ()

RETURNS DESCRIPTION

A decorator that can be used to define a PATCH request handler method.

Examples:

Partial update with JSON:

@json
@patch("users/{username}")
def update_user_partially(self, username, **updates: Body):
    """Partially update a user."""

Update specific fields:

@form_url_encoded
@patch("users/{username}")
def update_status(self, username, status: Field):
    """Update a user's status."""
Source code in uplink/commands.py
def patch(uri=None, args=()):
    """Creates a decorator that makes the decorated function an HTTP PATCH request handler.

    Args:
        uri (str, optional): The URI template for the request. Can include path parameters
            in the format {param_name}. If not provided, can be specified when the decorator is used.
        args (tuple|dict, optional): A sequence of argument annotations or mapping of
            argument names to annotations for the handler method.

    Returns:
        A decorator that can be used to define a PATCH request handler method.

    Examples:
        Partial update with JSON:

        ```python
        @json
        @patch("users/{username}")
        def update_user_partially(self, username, **updates: Body):
            \"""Partially update a user.\"""
        ```

        Update specific fields:

        ```python
        @form_url_encoded
        @patch("users/{username}")
        def update_status(self, username, status: Field):
            \"""Update a user's status.\"""
        ```
    """
    return HttpMethodFactory("PATCH")(uri, args)

uplink.delete

delete(uri=None, args=())

Creates a decorator that makes the decorated function an HTTP DELETE request handler.

PARAMETER DESCRIPTION
uri

The URI template for the request. Can include path parameters in the format {param_name}. If not provided, can be specified when the decorator is used.

TYPE: str DEFAULT: None

args

A sequence of argument annotations or mapping of argument names to annotations for the handler method.

TYPE: tuple | dict DEFAULT: ()

RETURNS DESCRIPTION

A decorator that can be used to define a DELETE request handler method.

Examples:

Basic usage:

@delete("users/{username}")
def delete_user(self, username):
    """Delete a specific user."""

With query parameters:

@delete("users/{username}/posts")
def delete_posts(self, username, before_date: Query = None):
    """Delete user's posts, optionally before a specific date."""
Source code in uplink/commands.py
def delete(uri=None, args=()):
    """Creates a decorator that makes the decorated function an HTTP DELETE request handler.

    Args:
        uri (str, optional): The URI template for the request. Can include path parameters
            in the format {param_name}. If not provided, can be specified when the decorator is used.
        args (tuple|dict, optional): A sequence of argument annotations or mapping of
            argument names to annotations for the handler method.

    Returns:
        A decorator that can be used to define a DELETE request handler method.

    Examples:
        Basic usage:

        ```python
        @delete("users/{username}")
        def delete_user(self, username):
            \"""Delete a specific user.\"""
        ```

        With query parameters:

        ```python
        @delete("users/{username}/posts")
        def delete_posts(self, username, before_date: Query = None):
            \"""Delete user's posts, optionally before a specific date.\"""
        ```
    """
    return HttpMethodFactory("DELETE")(uri, args)

uplink.head

head(uri=None, args=())

Creates a decorator that makes the decorated function an HTTP HEAD request handler.

PARAMETER DESCRIPTION
uri

The URI template for the request. Can include path parameters in the format {param_name}. If not provided, can be specified when the decorator is used.

TYPE: str DEFAULT: None

args

A sequence of argument annotations or mapping of argument names to annotations for the handler method.

TYPE: tuple | dict DEFAULT: ()

RETURNS DESCRIPTION

A decorator that can be used to define a HEAD request handler method.

Examples:

Basic usage:

@head("users/{username}")
def check_user(self, username):
    """Check if a user exists."""
Source code in uplink/commands.py
def head(uri=None, args=()):
    """Creates a decorator that makes the decorated function an HTTP HEAD request handler.

    Args:
        uri (str, optional): The URI template for the request. Can include path parameters
            in the format {param_name}. If not provided, can be specified when the decorator is used.
        args (tuple|dict, optional): A sequence of argument annotations or mapping of
            argument names to annotations for the handler method.

    Returns:
        A decorator that can be used to define a HEAD request handler method.

    Examples:
        Basic usage:

        ```python
        @head("users/{username}")
        def check_user(self, username):
            \"""Check if a user exists.\"""
        ```
    """
    return HttpMethodFactory("HEAD")(uri, args)

returns.*

Converting an HTTP response body into a custom Python object is straightforward with Uplink; the uplink.returns modules exposes optional decorators for defining the expected return type and data serialization format for any consumer method.

uplink.returns.json

json(type=None, key=(), model=None, member=())

Specifies that the decorated consumer method should return a JSON object.

# This method will return a JSON object (e.g., a dict or list)
@returns.json
@get("/users/{username}")
def get_user(self, username):
    """Get a specific user."""

Returning a Specific JSON Field

The key argument accepts a string or tuple that specifies the path of an internal field in the JSON document.

For instance, consider an API that returns JSON responses that, at the root of the document, contains both the server-retrieved data and a list of relevant API errors:

{
    "data": { "user": "prkumar", "id": "140232" },
    "errors": []
}

If returning the list of errors is unnecessary, we can use the key argument to strictly return the nested field data.id:

@returns.json(key=("data", "id"))
@get("/users/{username}")
def get_user_id(self, username):
    """Get a specific user's ID."""

We can also configure Uplink to convert the field before it's returned by also specifying the type argument:

@returns.json(key=("data", "id"), type=int)
@get("/users/{username}")
def get_user_id(self, username):
    """Get a specific user's ID."""

Added in version 0.5.0

Source code in uplink/returns.py
def __init__(self, type=None, key=(), model=None, member=()):
    if model:  # pragma: no cover
        warnings.warn(
            "The `model` argument of @returns.json is deprecated and will "
            "be removed in v1.0.0. Use `type` instead.",
            DeprecationWarning,
            stacklevel=2,
        )
    if member:  # pragma: no cover
        warnings.warn(
            "The `member` argument of @returns.json is deprecated and will "
            "be removed in v1.0.0. Use `key` instead.",
            DeprecationWarning,
            stacklevel=2,
        )
    self._type = type or model
    self._key = key or member

uplink.returns.from_json module-attribute

from_json = json

Specifies that the decorated consumer method should produce instances of a type class using a registered deserialization strategy (see uplink.loads.from_json)

This decorator accepts the same arguments as uplink.returns.json.

Often, a JSON response body represents a schema in your application. If an existing Python object encapsulates this schema, use the :py:attr:type argument to specify it as the return type:

@returns.from_json(type=User)
@get("/users/{username}")
def get_user(self, username):
    """Get a specific user."""

For Python 3 users, you can alternatively provide a return value annotation. Hence, the previous code is equivalent to the following in Python 3:

@returns.from_json
@get("/users/{username}")
def get_user(self, username) -> User:
    """Get a specific user."""

Both usages typically require also registering a converter that knows how to deserialize the JSON into the specified type (see uplink.loads.from_json). This step is unnecessary if the type is defined using a library for which Uplink has built-in support, such as marshmallow.

Added in version 0.6.0

uplink.returns.schema

schema(type)

Specifies that the function returns a specific type of response.

The standard way to provide a consumer method's return type is to set it as the method's return annotation:

@get("/users/{username}")
def get_user(self, username) -> UserSchema:
    """Get a specific user."""

Alternatively, you can use this decorator instead:

@returns.schema(UserSchema)
@get("/users/{username}")
def get_user(self, username):
    """Get a specific user."""

To have Uplink convert response bodies into the desired type, you will need to define an appropriate converter (e.g., using uplink.loads).

Added in version 0.5.1

Source code in uplink/returns.py
def __init__(self, type):
    self._schema = type

retry.*

uplink.retry

uplink.retry.RetryBackoff

Base class for a strategy that calculates the timeout between retry attempts.

You can compose two RetryBackoff instances by using the | operator:

CustomBackoffA() | CustomBackoffB()

The resulting backoff strategy will first compute the timeout using the left-hand instance. If that timeout is None, the strategy will try to compute a fallback using the right-hand instance. If both instances return None, the resulting strategy will also return None.

retry.when

The default behavior of the uplink.retry decorator is to retry on any raised exception. To override the retry criteria, use the uplink.retry decorator's when argument to specify a retry condition exposed through the uplink.retry.when module:

from uplink.retry.when import raises

class GitHub(uplink.Consumer):
    # Retry when a client connection timeout occurs
    @uplink.retry(when=raises(retry.CONNECTION_TIMEOUT))
    @uplink.get("/users/{user}")
    def get_user(self, user):
        """Get user by username."""

Use the | operator to logically combine retry conditions:

from uplink.retry.when import raises, status

class GitHub(uplink.Consumer):
    # Retry when an exception is raised or the status code is 503
    @uplink.retry(when=raises(Exception) | status(503))
    @uplink.get("/users/{user}")
    def get_user(self, user):
        """Get user by username."""

uplink.retry.when.raises

raises(expected_exception)

Retry when a specific exception type is raised.

PARAMETER DESCRIPTION
expected_exception

The exception type that should trigger a retry.

Source code in uplink/retry/when.py
def __init__(self, expected_exception):
    self._expected_exception = expected_exception

uplink.retry.when.status

status(*status_codes)

Retry on specific HTTP response status codes.

PARAMETER DESCRIPTION
*status_codes

The status codes that should trigger a retry.

DEFAULT: ()

Source code in uplink/retry/when.py
def __init__(self, *status_codes):
    self._status_codes = status_codes

uplink.retry.when.status_5xx

Retry after receiving a 5xx (server error) response.

retry.backoff

Retrying failed requests typically involves backoff: the client can wait some time before the next retry attempt to avoid high contention on the remote service.

To this end, the uplink.retry decorator uses capped exponential backoff with jitter by default, To override this, use the decorator's backoff argument to specify one of the alternative approaches exposed through the uplink.retry.backoff module:

from uplink import retry, Consumer, get
from uplink.retry.backoff import fixed

class GitHub(Consumer):
   # Employ a fixed one second delay between retries.
   @retry(backoff=fixed(1))
   @get("user/{username}")
   def get_user(self, username):
      """Get user by username."""
from uplink.retry.backoff import exponential

class GitHub(uplink.Consumer):
    @uplink.retry(backoff=exponential(multiplier=0.5))
    @uplink.get("/users/{user}")
    def get_user(self, user):
        """Get user by username."""

You can implement a custom backoff strategy by extending the class uplink.retry.RetryBackoff:

from uplink.retry import RetryBackoff

class MyCustomBackoff(RetryBackoff):
    ...

class GitHub(uplink.Consumer):
    @uplink.retry(backoff=MyCustomBackoff())
    @uplink.get("/users/{user}")
    def get_user(self, user):
        pass

uplink.retry.backoff.jittered

jittered(base=2, multiplier=1, minimum=0, maximum=MAX_VALUE)

Waits using capped exponential backoff and full jitter.

The implementation is discussed in this AWS Architecture Blog post, which recommends this approach for any remote clients, as it minimizes the total completion time of competing clients in a distributed system experiencing high contention.

Source code in uplink/retry/backoff.py
def __init__(self, base=2, multiplier=1, minimum=0, maximum=MAX_VALUE):
    self._exp_backoff = exponential(base, multiplier, minimum, maximum)

uplink.retry.backoff.exponential

exponential(base=2, multiplier=1, minimum=0, maximum=MAX_VALUE)

Waits using capped exponential backoff, meaning that the delay is multiplied by a constant base after each attempt, up to an optional maximum value.

Source code in uplink/retry/backoff.py
def __init__(self, base=2, multiplier=1, minimum=0, maximum=MAX_VALUE):
    self._base = base
    self._multiplier = multiplier
    self._minimum = minimum
    self._maximum = maximum

uplink.retry.backoff.fixed

fixed(seconds)

Waits for a fixed number of seconds before each retry.

Source code in uplink/retry/backoff.py
def __init__(self, seconds):
    self._seconds = seconds

retry.stop

By default, the uplink.retry decorator will repeatedly retry the original request until a response is rendered. To override this behavior, use the uplink.retry decorator's stop argument to specify one of the strategies exposed in the uplink.retry.stop module:

from uplink.retry.stop import after_attempt

class GitHub(uplink.Consumer):
    @uplink.retry(stop=after_attempt(3))
    @uplink.get("/users/{user}")
    def get_user(self, user):

Use the | operator to logically combine strategies:

from uplink.retry.stop import after_attempt, after_delay

class GitHub(uplink.Consumer):
    # Stop after 3 attempts or after the backoff exceeds 10 seconds.
    @uplink.retry(stop=after_attempt(3) | after_delay(10))
    @uplink.get("/users/{user}")
    def get_user(self, user):
        pass

uplink.retry.stop.after_attempt

after_attempt(attempt)

Stops retrying after the specified number of attempts.

PARAMETER DESCRIPTION
attempt

The maximum number of retry attempts before stopping.

Source code in uplink/retry/stop.py
def __init__(self, attempt):
    self._max_attempt = attempt
    self._attempt = 0

uplink.retry.stop.after_delay

after_delay(delay)

Stops retrying after the backoff exceeds the specified delay in seconds.

PARAMETER DESCRIPTION
delay

The maximum delay in seconds before stopping retry attempts.

Source code in uplink/retry/stop.py
def __init__(self, delay):
    self._max_delay = delay

uplink.retry.stop.NEVER module-attribute

NEVER = _NeverStop()

Continuously retry until the server returns a successful response.

ratelimit

uplink.ratelimit.ratelimit

ratelimit(calls=15, period=900, raise_on_limit=False, group_by=BY_HOST_AND_PORT, clock=now)

A decorator that constrains a consumer method or an entire consumer to making a specified maximum number of requests within a defined time period (e.g., 15 calls every 15 minutes).

Note

The rate limit is enforced separately for each host-port combination. Logically, requests are grouped by host and port, and the number of requests within a time period are counted and capped separately for each group.

By default, when the limit is reached, the client will wait until the current period is over before executing any subsequent requests. If you'd prefer the client to raise an exception when the limit is exceeded, set the raise_on_limit argument.

PARAMETER DESCRIPTION
calls

The maximum number of allowed calls that the consumer can make within the time period.

DEFAULT: 15

period

The duration of each time period in seconds.

DEFAULT: 900

raise_on_limit

Either an exception to raise when the client exceeds the rate limit or a boolean. If True, a RateLimitExceeded exception is raised.

DEFAULT: False

Source code in uplink/ratelimit.py
def __init__(
    self,
    calls=15,
    period=900,
    raise_on_limit=False,
    group_by=BY_HOST_AND_PORT,
    clock=now,
):
    self._max_calls = max(1, min(sys.maxsize, math.floor(calls)))
    self._period = period
    self._clock = clock
    self._limiter_cache = {}
    self._group_by = utils.no_op if group_by is None else group_by

    if utils.is_subclass(raise_on_limit, Exception) or isinstance(
        raise_on_limit, Exception
    ):
        self._create_limit_reached_exception = raise_on_limit
    elif raise_on_limit:
        self._create_limit_reached_exception = self._create_rate_limit_exceeded
    else:
        self._create_limit_reached_exception = None

uplink.ratelimit.RateLimitExceeded

RateLimitExceeded(calls, period)

A request failed because it exceeded the client-side rate limit.

Source code in uplink/ratelimit.py
def __init__(self, calls, period):
    super().__init__(
        f"Exceeded rate limit of [{calls}] calls every [{period}] seconds."
    )