A Router is an ordered collection of route->target pairs. It is used to efficiently match WSGI requests against a number of routes and return the first target that satisfies the request. The target may be anything, usually a string, ID or callable object. A route consists of
| 287 | |
| 288 | |
| 289 | class Router(object): |
| 290 | """ A Router is an ordered collection of route->target pairs. It is used to |
| 291 | efficiently match WSGI requests against a number of routes and return |
| 292 | the first target that satisfies the request. The target may be anything, |
| 293 | usually a string, ID or callable object. A route consists of a path-rule |
| 294 | and a HTTP method. |
| 295 | |
| 296 | The path-rule is either a static path (e.g. `/contact`) or a dynamic |
| 297 | path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax |
| 298 | and details on the matching order are described in docs:`routing`. |
| 299 | """ |
| 300 | |
| 301 | default_pattern = '[^/]+' |
| 302 | default_filter = 're' |
| 303 | |
| 304 | #: The current CPython regexp implementation does not allow more |
| 305 | #: than 99 matching groups per regular expression. |
| 306 | _MAX_GROUPS_PER_PATTERN = 99 |
| 307 | |
| 308 | def __init__(self, strict=False): |
| 309 | self.rules = [] # All rules in order |
| 310 | self._groups = {} # index of regexes to find them in dyna_routes |
| 311 | self.builder = {} # Data structure for the url builder |
| 312 | self.static = {} # Search structure for static routes |
| 313 | self.dyna_routes = {} |
| 314 | self.dyna_regexes = {} # Search structure for dynamic routes |
| 315 | #: If true, static routes are no longer checked first. |
| 316 | self.strict_order = strict |
| 317 | self.filters = { |
| 318 | 're': lambda conf: (_re_flatten(conf or self.default_pattern), |
| 319 | None, None), |
| 320 | 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))), |
| 321 | 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))), |
| 322 | 'path': lambda conf: (r'.+?', None, None) |
| 323 | } |
| 324 | |
| 325 | def add_filter(self, name, func): |
| 326 | """ Add a filter. The provided function is called with the configuration |
| 327 | string as parameter and must return a (regexp, to_python, to_url) tuple. |
| 328 | The first element is a string, the last two are callables or None. """ |
| 329 | self.filters[name] = func |
| 330 | |
| 331 | rule_syntax = re.compile('(\\\\*)' |
| 332 | '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)' |
| 333 | '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)' |
| 334 | '(?::((?:\\\\.|[^\\\\>])+)?)?)?>))') |
| 335 | |
| 336 | def _itertokens(self, rule): |
| 337 | offset, prefix = 0, '' |
| 338 | for match in self.rule_syntax.finditer(rule): |
| 339 | prefix += rule[offset:match.start()] |
| 340 | g = match.groups() |
| 341 | if g[2] is not None: |
| 342 | depr(0, 13, "Use of old route syntax.", |
| 343 | "Use <name> instead of :name in routes.") |
| 344 | if len(g[0]) % 2: # Escaped wildcard |
| 345 | prefix += match.group(0)[len(g[0]):] |
| 346 | offset = match.end() |
no outgoing calls
no test coverage detected
searching dependent graphs…