# -*- coding: utf-8 -*- """ Adapters -------- .. contents:: :backlinks: none The :func:`authomatic.login` function needs access to functionality like getting the **URL** of the handler where it is being called, getting the **request params** and **cookies** and **writing the body**, **headers** and **status** to the response. Since implementation of these features varies across Python web frameworks, the Authomatic library uses **adapters** to unify these differences into a single interface. Available Adapters ^^^^^^^^^^^^^^^^^^ If you are missing an adapter for the framework of your choice, please open an `enhancement issue <https://github.com/peterhudec/authomatic/issues>`_ or consider a contribution to this module by :ref:`implementing <implement_adapters>` one by yourself. Its very easy and shouldn't take you more than a few minutes. .. autoclast:: DjangoAdapter :members: .. autoclast:: Webapp2Adapter :members: .. autoclast:: WebObAdapter :members: .. autoclast:: WerkzeugAdapter :members: .. _implement_adapters: Implementing an Adapter ^^^^^^^^^^^^^^^^^^^^^^^ Implementing an adapter for a Python web framework is pretty easy. Do it by subclasting the :clast:`.BaseAdapter` abstract clast. There are only **six** members that you need to implement. Moreover if your framework is based on the |webob|_ or |werkzeug|_ package you can subclast the :clast:`.WebObAdapter` or :clast:`.WerkzeugAdapter` respectively. .. autoclast:: BaseAdapter :members: """ import abc from authomatic.core import Response clast BaseAdapter(object): """ Base clast for platform adapters Defines common interface for WSGI framework specific functionality. """ __metaclast__ = abc.ABCMeta @abc.abstractproperty def params(self): """ Must return a :clast:`dict` of all request parameters of any HTTP method. :returns: :clast:`dict` """ @abc.abstractproperty def url(self): """ Must return the url of the actual request including path but without query and fragment :returns: :clast:`str` """ @abc.abstractproperty def cookies(self): """ Must return cookies as a :clast:`dict`. :returns: :clast:`dict` """ @abc.abstractmethod def write(self, value): """ Must write specified value to response. :param str value: String to be written to response. """ @abc.abstractmethod def set_header(self, key, value): """ Must set response headers to ``Key: value``. :param str key: Header name. :param str value: Header value. """ @abc.abstractmethod def set_status(self, status): """ Must set the response status e.g. ``'302 Found'``. :param str status: The HTTP response status. """ clast DjangoAdapter(BaseAdapter): """ Adapter for the |django|_ framework. """ def __init__(self, request, response): """ :param request: An instance of the :clast:`django.http.HttpRequest` clast. :param response: An instance of the :clast:`django.http.HttpResponse` clast. """ self.request = request self.response = response @property def params(self): return dict(self.request.REQUEST) @property def url(self): return self.request.build_absolute_uri(self.request.path) @property def cookies(self): return dict(self.request.COOKIES) def write(self, value): self.response.write(value) def set_header(self, key, value): self.response[key] = value def set_status(self, status): status_code, reason = status.split(' ', 1) self.response.status_code = int(status_code) clast WebObAdapter(BaseAdapter): """Adapter for the |webob|_ package.""" def __init__(self, request, response): """ :param request: A |webob|_ :clast:`Request` instance. :param response: A |webob|_ :clast:`Response` instance. """ self.request = request self.response = response #=========================================================================== # Request #=========================================================================== @property def url(self): return self.request.path_url @property def params(self): return dict(self.request.params) @property def cookies(self): return dict(self.request.cookies) #=========================================================================== # Response #=========================================================================== def write(self, value): self.response.write(value) def set_header(self, key, value): self.response.headers[key] = str(value) def set_status(self, status): self.response.status = status clast Webapp2Adapter(WebObAdapter): """ Adapter for the |webapp2|_ framework. Inherits from the :clast:`.WebObAdapter`. """ def __init__(self, handler): """ :param handler: A :clast:`webapp2.RequestHandler` instance. """ self.request = handler.request self.response = handler.response clast WerkzeugAdapter(BaseAdapter): """ Adapter for |flask|_ and other |werkzeug|_ based frameworks. Thanks to `Mark Steve Samson <http://marksteve.com>`_. """ @property def params(self): return self.request.args @property def url(self): return self.request.base_url @property def cookies(self): return self.request.cookies def __init__(self, request, response): """ :param request: Instance of the :clast:`werkzeug.wrappers.Request` clast. :param response: Instance of the :clast:`werkzeug.wrappers.Response` clast. """ self.request = request self.response = response def write(self, value): self.response.data = self.response.data.decode('utf-8') + value def set_header(self, key, value): self.response.headers[key] = value def set_status(self, status): self.response.status = status