Function Annotations

For programming in general, function parameters drive a function’s dynamic behavior; a function’s output depends normally on its inputs. With uplink, function arguments parametrize an HTTP request, and you indicate the dynamic parts of the request by appropriately annotating those arguments with the classes detailed in this section.

Path

class uplink.Path(name=None, type=None)[source]

Substitution of a path variable in a URI template.

URI template parameters are enclosed in braces (e.g., {name}). To map an argument to a declared URI parameter, use the Path annotation:

class TodoService(object):
    @get("/todos/{id}")
    def get_todo(self, todo_id: Path("id")): pass

Then, invoking get_todo with a consumer instance:

todo_service.get_todo(100)

creates an HTTP request with a URL ending in /todos/100.

Note

Any unannotated function argument that shares a name with a URL path parameter is implicitly annotated with this class at runtime.

For example, we could simplify the method from the previous example by matching the path variable and method argument names:

@get("/todos/{id}")
def get_todo(self, id): pass

Query

class uplink.Query(name=None, encoded=False, type=None, encode_none=None)[source]

Set a dynamic query parameter.

This annotation turns argument values into URL query parameters. You can include it as function argument annotation, in the format: <query argument>: uplink.Query.

If the API endpoint you are trying to query uses q as a query parameter, you can add q: uplink.Query to the consumer method to set the q search term at runtime.

Example

@get("/search/commits")
def search(self, search_term: Query("q")):
    """Search all commits with the given search term."""

To specify whether or not the query parameter is already URL encoded, use the optional encoded argument:

@get("/search/commits")
def search(self, search_term: Query("q", encoded=True)):
    """Search all commits with the given search term."""

To specify if and how None values should be encoded, use the optional encode_none argument:

@get("/search/commits")
def search(self, search_term: Query("q"),
           search_order: Query("o", encode_none="null")):
    """
    Search all commits with the given search term using the
    optional search order.
    """
Parameters:
  • encoded (bool, optional) – Specifies whether the parameter name and value are already URL encoded.
  • encode_none (str, optional) – Specifies an optional string with which None values should be encoded. If not specified, parameters with a value of None will not be sent.

QueryMap

class uplink.QueryMap(encoded=False, type=None)[source]

A mapping of query arguments.

If the API you are using accepts multiple query arguments, you can include them all in your function method by using the format: <query argument>: uplink.QueryMap

Example

@get("/search/users")
def search(self, **params: QueryMap):
    """Search all users."""
Parameters:encoded (bool, optional) – Specifies whether the parameter name and value are already URL encoded.

HeaderMap

class uplink.HeaderMap(type=None)[source]

Pass a mapping of header fields at runtime.

Field

class uplink.Field(name=None, type=None)[source]

Defines a form field to the request body.

Use together with the decorator uplink.form_url_encoded and annotate each argument accepting a form field with uplink.Field.

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

FieldMap

class uplink.FieldMap(type=None)[source]

Defines a mapping of form fields to the request body.

Use together with the decorator uplink.form_url_encoded and annotate each argument accepting a form field with uplink.FieldMap.

Example

@form_url_encoded
@post("/user/edit")
def create_post(self, **user_info: FieldMap):
    """Update the current user."""

Part

class uplink.Part(name=None, type=None)[source]

Marks an argument as a form part.

Use together with the decorator uplink.multipart and annotate each form part with uplink.Part.

Example

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

PartMap

class uplink.PartMap(type=None)[source]

A mapping of form field parts.

Use together with the decorator uplink.multipart and annotate each part of form parts with uplink.PartMap

Example

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

Body

class uplink.Body(type=None)[source]

Set the request body at runtime.

Use together with the decorator uplink.json. The method argument value will become the request’s body when annotated with uplink.Body.

Example

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

Url

class uplink.Url[source]

Sets a dynamic URL.

Provides the URL at runtime as a method argument. Drop the decorator parameter path from uplink.get and annotate the corresponding argument with uplink.Url

Example

@get
def get(self, endpoint: Url):
    """Execute a GET requests against the given endpoint"""

Timeout

class uplink.Timeout[source]

Passes a timeout as a method argument at runtime.

While uplink.timeout attaches static timeout to all requests sent from a consumer method, this class turns a method argument into a dynamic timeout value.

Example

@get("/user/posts")
def get_posts(self, timeout: Timeout() = 60):
    """Fetch all posts for the current users giving up after given
    number of seconds."""

Context

class uplink.Context(name=None, type=None)[source]

Defines a name-value pair that is accessible to middleware at runtime.

Request middleware can leverage this annotation to give users control over the middleware’s behavior.

Example

Consider a custom decorator @cache (this would be a subclass of uplink.decorators.MethodAnnotation):

@cache(hours=3)
@get("users/user_id")
def get_user(self, user_id)
    """Retrieves a single user."""

As its name suggests, the @cache decorator enables caching server responses so that, once a request is cached, subsequent identical requests can be served by the cache provider rather than adding load to the upstream service.

Importantly, the writers of the @cache decorators can allow users to pass their own cache provider implementation through an argument annotated with Context:

@cache(hours=3)
@get("users/user_id")
def get_user(self, user_id, cache_provider: Context)
    """Retrieves a single user."""

To add a name-value pair to the context of any request made from a Consumer instance, you can use the Consumer.session.context property. Alternatively, you can annotate a constructor argument of a Consumer subclass with Context, as explained here.