Source code for jacquard.service.base

"""Base for HTTP endpoint plugins."""

import abc
import copy

import werkzeug.routing


[docs]class Endpoint(metaclass=abc.ABCMeta): """ Base HTTP endpoint. Subclasses must implement `url` and `handle`. `url` is a URL pattern in the standard Werkzeug/Flask form, and `handle` is the method which is actually called to dispatch the endpoint. Instances have two states: bound and unbound. When the endpoint is loaded it is instantiated in an unbound state. Before it's actually *dispatched*, the dispatcher calls `bind` which copies the endpoint to produce a bound version. Bound endpoints have context available in attributes: `reverse` and `request`. """ def __init__(self, config): """Constructor from system config.""" self.config = config @abc.abstractproperty def url(self): """ URL config, to be overridden in subclasses. Takes the standard Werkzeug/Flask format. Examples: * '/' * '/foo/bar/bazz' * '/foo/<user>' * '/order/<id:int>' Full documentation can be found in Werkzeug's `werkzeug.routing` docs. """ raise NotImplementedError
[docs] @abc.abstractclassmethod def handle(self, **kwargs): """ Endpoint handler. Any URL parameters are passed in as keyword arguments. This is only called on bound instances, so you can rely on `self.request` and friends existing. Return JSON structures. """ raise NotImplementedError
def __call__(self, **kwargs): """Convenience alias for `handle`.""" return self.handle(**kwargs) @property def defaults(self): """Default values for URL parameters.""" return {}
[docs] def build_rule(self, name): """Build `Rule` instance which represents this unbound endpoint.""" return werkzeug.routing.Rule( self.url, defaults=self.defaults, endpoint=self, )
[docs] def bind(self, request, reverse): """ Create bound version of this endpoint. Clones with `copy.copy` and assigns `instance.request` and `instance.reverse`. """ instance = copy.copy(self) instance._request = request instance._reverse = reverse return instance
@property def request(self): """Request this endpoint is handling.""" try: return self._request except AttributeError: raise AttributeError( "Unbound endpoint: `request` is only available on bound " "endpoints", )
[docs] def reverse(self, name, **kwargs): """Look up URL for a given endpoint with given kwargs.""" try: reverse = self._reverse except AttributeError: raise AttributeError( "Unbound endpoint: `reverse` is only available on bound " "endpoints", ) return reverse(name, **kwargs)