class Middleware(with_metaclass(ABCMeta, object)): """Base class for middleware implementations. Each request handler is assigned a list of `Middleware` implementations. Before a handler is called, each middleware is executed using the `Middleware.before` method. When the underlying handler is finished, the `Middleware.after` method may manipulate the result. """ def __init__(self, *args, **kwargs): """Initialize the decorator and register the `after()` callback.""" pass def __call__(self, fn): """Call the `before()` method and then the decorated method. If this returns a `Future`, add the `after()` method as a `done` callback. Otherwise execute it immediately. """ @coroutine @wraps(fn) def before(other, *args, **kwargs): before_result = yield self.before(other, args, kwargs) if isinstance(before_result, (ReturnInformationT, Model)): raise Return(before_result) result = yield fn(other, *args, **kwargs) after_result = yield self.after(other, args, kwargs, result) if isinstance(after_result, (ReturnInformationT, Model)): raise Return(after_result) raise Return(result) return before @abstractmethod def before(self, handler, args, kwargs): """Method executed before the underlying request handler is called.""" @abstractmethod def after(self, handler, args, kwargs, result): """Method executed after the unterlying request handler ist called."""
class ConsumerBase(with_metaclass(ConsumerMeta, object)): """Base class for content type consumers. In order to create a new consumer, you must create a new class that inherits from :py:class:`ConsumerBase` and sets the :data:`ConsumerBase.CONTENT_TYPE` variable:: class MyConsumer(s.ConsumerBase): CONTENT_TYPE = s.ContentType('application/xml') def consume(self, handler, model): return model(lxml.from_string(handler.request.body)) .. seealso:: :py:mod:`supercell.api.consumer.JsonConsumer.consume` """ CONTENT_TYPE = None """The target content type for the consumer. :type: `supercell.api.ContentType` """ @staticmethod def map_consumer(content_type, handler): """Map a given content type to the correct provider implementation. If no provider matches, raise a `NoProviderFound` exception. :param accept_header: HTTP Accept header value :type accept_header: str :param handler: supercell request handler :raises: :exc:`NoConsumerFound` """ accept = parse_accept_header(content_type) if len(accept) == 0: raise NoConsumerFound() (ctype, params, q) = accept[0] if ctype not in handler._CONS_CONTENT_TYPES: raise NoConsumerFound() c = ContentType(ctype, vendor=params.get('vendor', None), version=params.get('version', None)) if c not in handler._CONS_CONTENT_TYPES[ctype]: raise NoConsumerFound() known_types = [ t for t in ConsumerMeta.KNOWN_CONTENT_TYPES[ctype] if t[0] == c ] if len(known_types) == 1: return (handler._CONS_MODEL[c], known_types[0][1]) raise NoConsumerFound() def consume(self, handler, model): """This method should return the correct representation as a parsed model. :param model: the model to convert to a certain content type :type model: :class:`schematics.models.Model` """ raise NotImplementedError
class ProviderBase(with_metaclass(ProviderMeta, object)): """Base class for content type providers. Creating a new provider is just as simple as creating new consumers:: class MyProvider(s.ProviderBase): CONTENT_TYPE = s.ContentType('application/xml') def provide(self, model, handler): self.set_header('Content-Type', 'application/xml') handler.write(model.to_xml()) """ CONTENT_TYPE = None """The target content type for the provider. :type: `supercell.api.ContentType` """ @staticmethod def map_provider(accept_header, handler, allow_default=False): """Map a given content type to the correct provider implementation. If no provider matches, raise a `NoProviderFound` exception. :param accept_header: HTTP Accept header value :type accept_header: str :param handler: supercell request handler :param allow_default: allow usage of default provider if no accept header is set, default is False :type allow_default: bool :raises: :exc:`NoProviderFound` :return: A tuple of the matching provider implementation class and the provide()-kwargs :rtype: (supercell.api.provider.ProviderBase, dict) """ if not hasattr(handler, '_PROD_CONTENT_TYPES'): raise NoProviderFound() accept = parse_accept_header(accept_header) if len(accept) > 0: for (ctype, params, q) in accept: if ctype not in handler._PROD_CONTENT_TYPES: continue c = ContentType(ctype, vendor=params.get('vendor', None), version=params.get('version', None)) if c not in handler._PROD_CONTENT_TYPES[ctype]: continue known_types = [t for t in ProviderMeta.KNOWN_CONTENT_TYPES[ctype] if t[0] == c] configuration = handler._PROD_CONFIGURATION[ctype] if len(known_types) == 1: return (known_types[0][1], configuration) if allow_default and 'default' in handler._PROD_CONTENT_TYPES: content_type = handler._PROD_CONTENT_TYPES['default'] configuration = handler._PROD_CONFIGURATION['default'] ctype = content_type.content_type default_type = [t for t in ProviderMeta.KNOWN_CONTENT_TYPES[ctype] if t[0] == content_type] if len(default_type) == 1: return (default_type[0][1], configuration) raise NoProviderFound() def provide(self, model, handler, **kwargs): """This method should return the correct representation as a simple string (i.e. byte buffer) that will be used as return value. :param model: the model to convert to a certain content type :type model: supercell.schematics.Model :param handler: the handler to write the return :type handler: supercell.requesthandler.RequestHandler """ raise NotImplementedError def error(self, status_code, message, handler): """This method should return the correct representation of errors that will be used as return value. :param status_code: the HTTP status code to return :type status_code: int :param message: the error message to return :type message: str :param handler: the handler to write the return :type handler: supercell.requesthandler.RequestHandler """ raise NotImplementedError
class ProviderBase(with_metaclass(ProviderMeta, object)): """Base class for content type providers. Creating a new provider is just as simple as creating new consumers:: class MyProvider(s.ProviderBase): CONTENT_TYPE = s.ContentType('application/xml') def provide(self, model, handler): self.set_header('Content-Type', 'application/xml') handler.write(model.to_xml()) """ CONTENT_TYPE = None """The target content type for the provider. :type: `supercell.api.ContentType` """ @staticmethod def map_provider(accept_header, handler, allow_default=False): """Map a given content type to the correct provider implementation. If no provider matches, raise a `NoProviderFound` exception. :param accept_header: HTTP Accept header value :type accept_header: str :param handler: supercell request handler :raises: :exc:`NoProviderFound` """ if not hasattr(handler, '_PROD_CONTENT_TYPES'): raise NoProviderFound() accept = parse_accept_header(accept_header) if len(accept) > 0: for (ctype, params, q) in accept: if ctype not in handler._PROD_CONTENT_TYPES: continue c = ContentType(ctype, vendor=params.get('vendor', None), version=params.get('version', None)) if c not in handler._PROD_CONTENT_TYPES[ctype]: continue known_types = [ t for t in ProviderMeta.KNOWN_CONTENT_TYPES[ctype] if t[0] == c ] if len(known_types) == 1: return known_types[0][1] if 'default' in handler._PROD_CONTENT_TYPES: content_type = handler._PROD_CONTENT_TYPES['default'] ctype = content_type.content_type default_type = [ t for t in ProviderMeta.KNOWN_CONTENT_TYPES[ctype] if t[0] == content_type ] if len(default_type) == 1: return default_type[0][1] raise NoProviderFound() def provide(self, model, handler): """This method should return the correct representation as a simple string (i.e. byte buffer) that will be used as return value. :param model: the model to convert to a certain content type :type model: supercell.schematics.Model """ raise NotImplementedError