_=self.locale.translate,
static_url=self.static_url,
xsrf_form_html=self.xsrf_form_html,
+ reverse_url=self.application.reverse_url
)
args.update(self.ui)
args.update(kwargs)
http_server.listen(8080)
ioloop.IOLoop.instance().start()
- The constructor for this class takes in a list of (regexp, request_class)
- tuples. When we receive requests, we iterate over the list in order and
- instantiate an instance of the first request class whose regexp matches
- the request path.
+ The constructor for this class takes in a list of URLSpec objects
+ or (regexp, request_class) tuples. When we receive requests, we
+ iterate over the list in order and instantiate an instance of the
+ first request class whose regexp matches the request path.
Each tuple can contain an optional third element, which should be a
dictionary if it is present. That dictionary is passed as keyword
else:
self.transforms = transforms
self.handlers = []
+ self.named_handlers = {}
self.default_host = default_host
self.settings = settings
self.ui_modules = {}
handlers = []
self.handlers.append((re.compile(host_pattern), handlers))
- for handler_tuple in host_handlers:
- assert len(handler_tuple) in (2, 3)
- pattern = handler_tuple[0]
- handler = handler_tuple[1]
- if len(handler_tuple) == 3:
- kwargs = handler_tuple[2]
- else:
- kwargs = {}
- if not pattern.endswith("$"):
- pattern += "$"
- handlers.append((re.compile(pattern), handler, kwargs))
+ for spec in host_handlers:
+ if type(spec) is type(()):
+ assert len(spec) in (2, 3)
+ pattern = spec[0]
+ handler = spec[1]
+ if len(spec) == 3:
+ kwargs = spec[2]
+ else:
+ kwargs = {}
+ spec = URLSpec(pattern, handler, kwargs)
+ handlers.append(spec)
+ if spec.name:
+ self.named_handlers[handler.name] = spec
def add_transform(self, transform_class):
"""Adds the given OutputTransform to our transform list."""
handler = RedirectHandler(
request, "http://" + self.default_host + "/")
else:
- for pattern, handler_class, kwargs in handlers:
- match = pattern.match(request.path)
+ for spec in handlers:
+ match = spec.regex.match(request.path)
if match:
- handler = handler_class(self, request, **kwargs)
+ handler = spec.handler_class(self, request, **spec.kwargs)
args = match.groups()
break
if not handler:
handler._execute(transforms, *args)
return handler
+ def reverse_url(self, name, *args):
+ """Returns a URL path for handler named `name`
+
+ The handler must be added to the application as a named URLSpec
+ """
+ if name in self.named_handlers:
+ return self.named_handlers[name].reverse(*args)
+ raise KeyError("%s not found in named urls" % name)
class HTTPError(Exception):
"""An exception that will turn into an HTTP error response."""
def render_string(self, path, **kwargs):
return self.handler.render_string(path, **kwargs)
+class URLSpec(object):
+ """Specifies mappings between URLs and handlers."""
+ def __init__(self, pattern, handler_class, kwargs=None, name=None):
+ """Creates a URLSpec.
+
+ Parameters:
+ pattern: Regular expression to be matched. Any groups in the regex
+ will be passed in to the handler's get/post/etc methods as
+ arguments.
+ handler_class: RequestHandler subclass to be invoked.
+ kwargs (optional): A dictionary of additional arguments to be passed
+ to the handler's constructor.
+ name (optional): A name for this handler. Used by
+ Application.reverse_url.
+ """
+ if not pattern.endswith('$'):
+ pattern += '$'
+ self.regex = re.compile(pattern)
+ self.handler_class = handler_class
+ self.kwargs = kwargs
+ self.name = name
+ self._path, self._group_count = self._find_groups()
+
+ def _find_groups(self):
+ """Returns a tuple (reverse string, group count) for a url.
+
+ For example: Given the url pattern /([0-9]{4})/([a-z-]+)/, this method
+ would return ('/%s/%s/', 2).
+ """
+ pattern = self.regex.pattern
+ if pattern.startswith('^'):
+ pattern = pattern[1:]
+ if pattern.endswith('$'):
+ pattern = pattern[:-1]
+
+ if self.regex.groups != pattern.count('('):
+ # The pattern is too complicated for our simplistic matching,
+ # so we can't support reversing it.
+ return (None, None)
+
+ pieces = []
+ for fragment in pattern.split('('):
+ if ')' in fragment:
+ paren_loc = fragment.index(')')
+ if paren_loc >= 0:
+ pieces.append('%s' + fragment[paren_loc + 1:])
+ else:
+ pieces.append(fragment)
+
+ return (''.join(pieces), self.regex.groups)
+
+ def reverse(self, *args):
+ assert self._path is not None, \
+ "Cannot reverse url regex " + self.regex.pattern
+ assert len(args) == self._group_count, "required number of arguments "\
+ "not found"
+ if not len(args):
+ return self._path
+ return self._path % tuple([str(a) for a in args])
+
+url = URLSpec
def _utf8(s):
if isinstance(s, unicode):