diff options
author | Marcel Hellkamp <marc@gsites.de> | 2013-11-01 02:40:19 +0100 |
---|---|---|
committer | Marcel Hellkamp <marc@gsites.de> | 2013-11-01 02:40:19 +0100 |
commit | a008b0da8db191c31dd4418e2867644436b10ce9 (patch) | |
tree | 8b2b1062dfc543d8200b0363a190d1c4e19ab96a | |
parent | 154369b2b9f7393ca9d3d73de7e046e2342cf00a (diff) | |
download | bottle-rgroups.tar.gz |
Route Groups (wip)rgroups
-rw-r--r-- | bottle.py | 81 |
1 files changed, 67 insertions, 14 deletions
@@ -463,7 +463,7 @@ class Route(object): #: The original callback with no plugins applied. Useful for introspection. self.callback = callback #: The name of the route (if specified) or ``None``. - self.name = name or None + self.names = makelist(name) #: A list of route-specific plugins (see :meth:`Bottle.route`). self.plugins = plugins or [] #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). @@ -473,6 +473,10 @@ class Route(object): #: plugin configuration and meta-data. self.config = ConfigDict().load_dict(config) + @property + def name(self): + return self.names[-1] if self.names else None + def __call__(self, *a, **ka): depr("Some APIs changed to return Route() instances instead of"\ " callables. Make sure to use the Route.call method and not to"\ @@ -503,14 +507,8 @@ class Route(object): def all_plugins(self): ''' Yield all Plugins affecting this route. ''' - unique = set() - for p in reversed(self.app.plugins + self.plugins): - if True in self.skiplist: break - name = getattr(p, 'name', False) - if name and (name in self.skiplist or name in unique): continue - if p in self.skiplist or type(p) in self.skiplist: continue - if name: unique.add(name) - yield p + return _filter_plugins(reversed(self.app.plugins + self.plugins), + self.skiplist) def _make_callback(self): callback = self.callback @@ -556,6 +554,15 @@ class Route(object): return '<%s %r %r>' % (self.method, self.rule, cb) +def _filter_plugins(plugins, skiplist): + unique = set() + for plugin in plugins: + if True in skiplist: break + name = getattr(plugin, 'name', False) + if name and (name in skiplist or name in unique): continue + if plugin in skiplist or type(plugin) in skiplist: continue + if name: unique.add(name) + yield p @@ -576,6 +583,8 @@ class Bottle(object): def __init__(self, catchall=True, autojson=True): + self._group_stack = []; + #: A :class:`ConfigDict` for app specific configuration. self.config = ConfigDict() self.config._on_change = functools.partial(self.trigger_hook, 'config') @@ -596,6 +605,7 @@ class Bottle(object): if self.config['autojson']: self.install(JSONPlugin()) self.install(TemplatePlugin()) + #: If true, most exceptions are caught and returned as :exc:`HTTPError` catchall = DictProperty('config', 'catchall') @@ -760,9 +770,35 @@ class Bottle(object): location = self.router.build(routename, **kargs).lstrip('/') return urljoin(urljoin('/', scriptname), location) + def group(self, prefix=None, apply=None, skip=None, name=None): + if not prefix: + prefix = '?' + elif '?' not in prefix: + prefix = prefix + '?' + apply = makelist(apply) + skip = makelist(skip) + return GroupContext(app, locals()) + + def _enter_group(self, group): + self._group_stack.append(group) + + def _exit_group(self, group): + self._group_stack.pop() + + def _group_apply(self, route): + chain = reversed(filter(None, g.prefix for g in self._group_stack)) + for group in reversed(self._group_stack): + route.rule = group.prefix.replace('?', route.rule) + for group in self._group_stack: + route.plugins.extend(group.apply) + route.skiplist.extend(group.skip) + if group.name: + route.names.insert(0, group.name) + def add_route(self, route): ''' Add a route object, but do not change the :data:`Route.app` attribute.''' + self._group_apply(route) self.routes.append(route) self.router.add(route.rule, route.method, route, name=route.name) if DEBUG: route.prepare() @@ -797,13 +833,13 @@ class Bottle(object): if callable(path): path, callback = None, path plugins = makelist(apply) skiplist = makelist(skip) + def decorator(callback): - # TODO: Documentation and tests if isinstance(callback, basestring): callback = load(callback) - for rule in makelist(path) or yieldroutes(callback): + for rule in makelist(path) or list(yieldroutes(callback): for verb in makelist(method): verb = verb.upper() - route = Route(self, rule, verb, callback, name=name, + route = self.build_route(rule, verb, callback, name=name, plugins=plugins, skiplist=skiplist, **config) self.add_route(route) return callback @@ -2105,7 +2141,6 @@ class ConfigDict(dict): return self - class AppStack(list): """ A stack-like list. Calling it returns the head of the stack. """ @@ -2113,7 +2148,7 @@ class AppStack(list): """ Return the current default application. """ return self[-1] - def push(self, value=None): + def push(self, value=None, meta=None): """ Add a new :class:`Bottle` instance to the stack """ if not isinstance(value, Bottle): value = Bottle() @@ -2121,6 +2156,24 @@ class AppStack(list): return value +class GroupContext(object): + ''' The context manager returned by :meth:`Bottle.group` ''' + + def __init__(self, app, **meta): + self.__dict__.update(meta) + self.app = app + + def __enter__(self): + default_app.push(self.app) + self.app._enter_group(self) + return self.app + + def __exit__(self, exc_type, exc_value, traceback): + self.app._exit_group(self) + default_app.pop() + return + + class WSGIFileWrapper(object): def __init__(self, fp, buffer_size=1024*64): |