labthings_fastapi.middleware.url_for

Middleware to make url_for available as a context variable.

This module is intended mostly for internal use within LabThings. The short summary is that, if you need to refer to other endpoints in the LabThings server, you should not return hard-coded URLs, but instead use a URLFor object. This will be converted to a URL when it’s serialised by FastAPI, using the correct url_for function for the current request.

Under the hood, this module defines a url_for function that performs the conversion. This function may only be run in certain places in the code, as it relies on a context variable. As a rule of thumb, it’s OK to call url_for from a serialiser of a pydantic model, but you should not call it from within an Action or Property.

There are several places in LabThings where we need to be able to include URLs to other endpoints in the LabThings server, most notably in the output of Actions. For example, if an Action outputs a Blob, the URL to download that Blob would need to be generated.

Actions are particularly complicated, as they are often invoked by one HTTP request, and polled by subsequent requests. In order to ensure that the URL we generate is consistent with the URL being requested, we should always use the url_for method from the HTTP request we are responding to. This means it is, in general, not a great idea to generate URLs within an Action and hold on to them as strings. While it will work most of the time, it would be better to store the endpoint name, and only convert it to a URL when the action’s output is serialised by FastAPI.

This module includes a ContextVar for the url_for function, and provides a middleware function that sets the context variable for every request, and a custom type that works with pydantic to convert endpoint names to URLs at serialisation time.

Attributes

url_for_ctx

Context variable storing the url_for function for the current request.

Classes

URLFor

A pydantic-compatible type that converts endpoint names to URLs.

Functions

set_url_for_context(→ collections.abc.Iterator[None])

Set the url_for context variable for the duration of the context.

dummy_url_for(→ starlette.datastructures.URL)

Generate a fake URL as a placeholder for a real url_for function.

url_for(→ starlette.datastructures.URL)

Get a URL for the given endpoint name and path parameters.

url_for_middleware(→ fastapi.Response)

Middleware to set the url_for context variable for each request.

Module Contents

labthings_fastapi.middleware.url_for.url_for_ctx: contextvars.ContextVar[collections.abc.Callable[Ellipsis, starlette.datastructures.URL]]

Context variable storing the url_for function for the current request.

labthings_fastapi.middleware.url_for.set_url_for_context(url_for_function: collections.abc.Callable[Ellipsis, starlette.datastructures.URL]) collections.abc.Iterator[None]

Set the url_for context variable for the duration of the context.

Parameters:

url_for_function – The url_for function to set in the context variable.

labthings_fastapi.middleware.url_for.dummy_url_for(endpoint: str, **params: Any) starlette.datastructures.URL

Generate a fake URL as a placeholder for a real url_for function.

This is intended for use in test code.

Parameters:
  • endpoint – The name of the endpoint.

  • **params – The path parameters.

Returns:

A fake URL.

labthings_fastapi.middleware.url_for.url_for(endpoint_name: str, **params: Any) starlette.datastructures.URL

Get a URL for the given endpoint name and path parameters.

This function uses the url_for function stored in a context variable to convert endpoint names and parameters to URLs. It is intended to have the same signature as fastapi.Request.url_for.

This function will raise a NoUrlForContextError if there is no url_for function in the context variable. This will be the case if the function is called outside of a request handler. As a rule, this function should not be called from within Actions or Properties.

URLFor is provided as a safe way to return URLs: it ensures that the URL is only generated at serialisation time, when there is a valid url_for function in the context. This also means the URL is always correct for the request being handled.

Parameters:
  • endpoint_name – The name of the endpoint to generate a URL for.

  • **params – The path parameters to use in the URL.

Returns:

The generated URL.

Raises:

NoUrlForContextError – if there is no url_for function in the context.

async labthings_fastapi.middleware.url_for.url_for_middleware(request: fastapi.Request, call_next: collections.abc.Callable[[fastapi.Request], collections.abc.Awaitable[fastapi.Response]]) fastapi.Response

Middleware to set the url_for context variable for each request.

This middleware retrieves the url_for function from the incoming request, and sets it in the context variable for the duration of the request.

Parameters:
  • request – The incoming FastAPI request.

  • call_next – The next middleware or endpoint handler to call.

Returns:

The response from the next handler.

class labthings_fastapi.middleware.url_for.URLFor(endpoint_name: str, **params: Any)

A pydantic-compatible type that converts endpoint names to URLs.

This class is intended to be used as a field type in pydantic models or as a return type from actions or properties. It does not convert endpoint names to URLs immediately, but instead stores the endpoint name and parameters, and only generates the URL when it is serialised by FastAPI.

It is safe to create a URLFor instance anywhere, but converting it to a string (i.e. generating the URL) requires a valid url_for function and should generally be left for FastAPI.

Fields or return values annotated as URLFor will only accept a URLFor instance, but will be serialised to JSON as a string, and will show up in the JSONSchema as a string.

Validating a string, i.e. converting a string to a URLFor instance, is not supported, and will raise a TypeError.

Create a URLFor instance.

Parameters:
  • endpoint_name – The name of the endpoint to generate a URL for.

  • **params – The path parameters to use in the URL.

endpoint_name
params
__str__() str

Convert the URLFor instance to a URL string.

Returns:

The generated URL as a string.

classmethod __get_pydantic_core_schema__(source: type[Any], handler: pydantic.GetCoreSchemaHandler) pydantic_core.core_schema.CoreSchema

Get the pydantic core schema for the URLFor type.

This magic method allows pydantic to serialise URLFor instances, and generate a JSONSchema for them. Currently, URLFor instances may not be validated from strings, and attempting to do so will raise an error.

The “core schema” we generate describes the field as a string, and serialises it by calling str(obj) which in turn calls our __str__ method to generate the URL.

Parameters:
  • source – The source type being converted.

  • handler – The pydantic core schema handler.

Returns:

The pydantic core schema for the URLFor type.

classmethod _validate(value: Any, handler: collections.abc.Callable[[Any], Self]) Self

Validate and convert a value to a URLFor instance.

Parameters:
  • value – The value to validate.

  • handler – The handler to convert the value if needed.

Returns:

The validated URLFor instance.

Raises:

ValueError – if the value is not a URLFor instance.