import inspect
import werkzeug
from . import lib
from . import request
from . import response
from . import errors
[docs]class App(object):
"""
Resource application, provide routing and execution
:param list hooks: List of hook classes (check :py:mod:`.hooks`)
:param list resources: Expected items `(path, resource class, [namespace])`
:param config: optional configuration values (updated :py:mod:`.conf`)
"""
hooks = []
#: List of rules, will be **extended** by App(resources=[])
#: Tuple should be presented: ('path', Resource, [namespace])
resources = []
def __init__(self, hooks=None, resources=None, **config):
#: Store the configuration (copied from :py:mod:`.conf`)
self.config = lib.get_config(getattr(self, 'config', {}))
self.functions = {}
if hooks is not None:
self.hooks = hooks
self.config.update(config)
self.rules = werkzeug.routing.Map()
for resource in self.resources:
self.add(*resource)
for resource in resources or ():
self.add(*resource)
self.adapter = self.rules.bind(self['host'])
self.setup_hooks()
def __getitem__(self, name):
return self.config[name]
[docs] def dispatch(
self, path_info, method, query=None, body=None, headers=None,
cookies=None, session=None
):
opts = None
req = None
try:
endpoint, path = self.adapter.match(path_info, method)
func = self.functions[endpoint]
opts = lib.get_options(func)
req = request.Request(
opts, self, path, query, body, headers, cookies, session
)
kwargs = req.build()
except Exception as ex:
res = self.handle_client_exceptions(
ex, path_info, method, opts, req
)
return res.build()
try:
content = func(**kwargs)
res = response.Response(content, self, opts, req)
return res.build()
except Exception as ex:
res = self.handle_exception(ex, opts, req)
return res.build()
[docs] def add(self, path, resource, prefix=''):
if inspect.isfunction(resource):
self._add_function(path, resource, prefix)
elif inspect.isclass(resource):
self._add_class(path, resource(), prefix)
else:
self._add_class(path, resource, prefix)
[docs] def handle_client_exceptions(
self, ex, path_info, method, opts=None, req=None
):
ex = self.transform_exception(ex)
res = errors.ErrorResponse(ex, self, opts, req)
return res
[docs] def handle_exception(self, ex, opts, req):
ex = self.transform_exception(ex)
res = errors.ErrorResponse(ex, self, opts, req)
return res
[docs] def add_rule(self, rule):
self.rules.add(rule)
[docs] def set_function(self, name, resource):
self.functions[name] = resource
[docs] def setup_hooks(self):
pass
[docs] def _add_class(self, path, resource, prefix=''):
members = lib.get_resource_members(resource)
if not members:
raise ValueError(
"There is no endpoint in the given resource: %s" % resource
)
if not prefix:
prefix = getattr(resource, '_name', lib.get_fqname(resource))
for member in members:
opts = lib.get_options(member)
self._add_function(path+opts['path'], member, prefix)
[docs] def _add_function(self, path, resource, prefix=''):
opts = lib.get_options(resource)
if opts:
if prefix:
prefix += '#'
name = prefix+opts['name']
rule = self._make_rule(path, opts['methods'], name)
self.add_rule(rule)
self.set_function(name, resource)
else:
raise ValueError(
"The given function (%s) is not and endpoint endpoint"
% resource
)
[docs] def _make_rule(self, path, methods, endpoint):
return werkzeug.routing.Rule(path, methods=methods, endpoint=endpoint)