diff options
-rw-r--r-- | CHANGELOG | 5 | ||||
-rw-r--r-- | routes/mapper.py | 22 | ||||
-rw-r--r-- | routes/route.py | 73 | ||||
-rw-r--r-- | tests/test_functional/test_recognition.py | 4 |
4 files changed, 82 insertions, 22 deletions
@@ -1,6 +1,11 @@ Routes Changelog %%%%%%%%%%%%%%%% +Release 1.12 (**tip**) +====================== +* Adding regular expression builder for entire regexp for faster rejection + in a single regexp match should none of the routes match. + Release 1.11 (September 28, 2009) ================================= * Extensive documentation rewrite. diff --git a/routes/mapper.py b/routes/mapper.py index 1b1940c..7b2342b 100644 --- a/routes/mapper.py +++ b/routes/mapper.py @@ -138,6 +138,7 @@ class Mapper(object): self.urlcache = LRUCache(1600) self._created_regs = False self._created_gens = False + self._master_regexp = None self.prefix = None self.req_data = threadinglocal.local() self.directory = directory @@ -359,15 +360,29 @@ class Mapper(object): clist = [] else: clist = self.controller_scan - + for key, val in self.maxkeys.iteritems(): for route in val: route.makeregexp(clist) + regexps = [] + routematches = [] + for route in self.matchlist: + if not route.static: + routematches.append(route) + regexps.append(route.makeregexp(clist, include_names=False)) + self._routematches = routematches # Create our regexp to strip the prefix if self.prefix: self._regprefix = re.compile(self.prefix + '(.*)') + + # Save the master regexp + regexp = '|'.join( + ['(?P<%s>%s)' % ('a' * (num + 1), x) for num, x in enumerate(regexps)] + ) + self._master_reg = regexp + self._master_regexp = re.compile(regexp) self._created_regs = True def _match(self, url): @@ -402,6 +417,11 @@ class Mapper(object): sub_domains_ignore = self.sub_domains_ignore domain_match = self.domain_match debug = self.debug + + valid_url = re.match(self._master_regexp, url) + if not valid_url: + return (None, None, matchlog) + for route in self.matchlist: if route.static: if debug: diff --git a/routes/route.py b/routes/route.py index cfceb0f..1995781 100644 --- a/routes/route.py +++ b/routes/route.py @@ -262,7 +262,7 @@ class Route(object): return (defaults, newdefaultkeys) - def makeregexp(self, clist): + def makeregexp(self, clist, include_names=True): """Create a regular expression for matching purposes Note: This MUST be called before match can function properly. @@ -272,9 +272,14 @@ class Route(object): framework after it knows all available controllers that can be utilized. + include_names indicates whether this should be a match regexp + assigned to itself using regexp grouping names, or if names + should be excluded for use in a single larger regexp to + determine if any routes match + """ if self.minimization: - reg = self.buildnextreg(self.routelist, clist)[0] + reg = self.buildnextreg(self.routelist, clist, include_names)[0] if not reg: reg = '/' reg = reg + '(/)?' + '$' @@ -282,14 +287,17 @@ class Route(object): if not reg.startswith('/'): reg = '/' + reg else: - reg = self.buildfullreg(clist) + reg = self.buildfullreg(clist, include_names) reg = '^' + reg + if not include_names: + return reg + self.regexp = reg self.regmatch = re.compile(reg) - def buildfullreg(self, clist): + def buildfullreg(self, clist, include_names=True): """Build the regexp by iterating through the routelist and replacing dicts with the appropriate regexp match""" regparts = [] @@ -302,13 +310,16 @@ class Route(object): partmatch = self.reqs.get(var) or '[^/]+?' else: partmatch = self.reqs.get(var) or '.+?' - regparts.append('(?P<%s>%s)' % (var, partmatch)) + if include_names: + regparts.append('(?P<%s>%s)' % (var, partmatch)) + else: + regparts.append('(%s)' % partmatch) else: regparts.append(re.escape(part)) regexp = ''.join(regparts) + '$' return regexp - def buildnextreg(self, path, clist): + def buildnextreg(self, path, clist, include_names=True): """Recursively build our regexp given a path, and a controller list. @@ -328,7 +339,7 @@ class Route(object): (rest, noreqs, allblank) = ('', True, True) if len(path[1:]) > 0: self.prior = part - (rest, noreqs, allblank) = self.buildnextreg(path[1:], clist) + (rest, noreqs, allblank) = self.buildnextreg(path[1:], clist, include_names) if isinstance(part, dict) and part['type'] == ':': var = part['name'] @@ -336,15 +347,26 @@ class Route(object): # First we plug in the proper part matcher if self.reqs.has_key(var): - partreg = '(?P<' + var + '>' + self.reqs[var] + ')' + if include_names: + partreg = '(?P<%s>%s)' % (var, self.reqs[var]) + else: + partreg = '(%s)' % self.reqs[var] elif var == 'controller': - partreg = '(?P<' + var + '>' + '|'.join(map(re.escape, clist)) - partreg += ')' + if include_names: + partreg = '(?P<%s>%s)' % (var, '|'.join(map(re.escape, clist))) + else: + partreg = '(%s)' % '|'.join(map(re.escape, clist)) elif self.prior in ['/', '#']: - partreg = '(?P<' + var + '>[^' + self.prior + ']+?)' + if include_names: + partreg = '(?P<' + var + '>[^' + self.prior + ']+?)' + else: + partreg = '([^' + self.prior + ']+?)' else: if not rest: - partreg = '(?P<' + var + '>[^%s]+?)' % '/' + if include_names: + partreg = '(?P<%s>[^%s]+?)' % (var, '/') + else: + partreg = '([^%s]+?)' % '/' else: end = ''.join(self.done_chars) rem = rest @@ -355,7 +377,10 @@ class Route(object): else: rem = end rem = frozenset(rem) | frozenset(['/']) - partreg = '(?P<' + var + '>[^%s]+?)' % ''.join(rem) + if include_names: + partreg = '(?P<%s>[^%s]+?)' % (var, ''.join(rem)) + else: + partreg = '([^%s]+?)' % ''.join(rem) if self.reqs.has_key(var): noreqs = False @@ -413,21 +438,31 @@ class Route(object): elif isinstance(part, dict) and part['type'] == '*': var = part['name'] if noreqs: - if self.defaults.has_key(var): - reg = '(?P<' + var + '>.*)' + rest + if include_names: + reg = '(?P<%s>.*)' % var + rest else: - reg = '(?P<' + var + '>.*)' + rest + reg = '(.*)' + rest + if not self.defaults.has_key(var): allblank = False noreqs = False else: if allblank and self.defaults.has_key(var): - reg = '(?P<' + var + '>.*)' + rest + if include_names: + reg = '(?P<%s>.*)' % var + rest + else: + reg = '(.*)' + rest elif self.defaults.has_key(var): - reg = '(?P<' + var + '>.*)' + rest + if include_names: + reg = '(?P<%s>.*)' % var + rest + else: + reg = '(.*)' + rest else: + if include_names: + reg = '(?P<%s>.*)' % var + rest + else: + reg = '(.*)' + rest allblank = False noreqs = False - reg = '(?P<' + var + '>.*)' + rest elif part and part[-1] in self.done_chars: if allblank: reg = re.escape(part[:-1]) + '(' + re.escape(part[-1]) + rest diff --git a/tests/test_functional/test_recognition.py b/tests/test_functional/test_recognition.py index 68a023c..a92cc11 100644 --- a/tests/test_functional/test_recognition.py +++ b/tests/test_functional/test_recognition.py @@ -651,7 +651,7 @@ class TestRecognition(unittest.TestCase): resultdict, route_obj, debug = m.routematch('/nowhere') eq_(None, resultdict) eq_(None, route_obj) - eq_(len(debug), 1) + eq_(len(debug), 0) def test_match_debug(self): m = Mapper() @@ -667,7 +667,7 @@ class TestRecognition(unittest.TestCase): resultdict, route_obj, debug = m.match('/nowhere') eq_(None, resultdict) eq_(route_obj, None) - eq_(len(debug), 1) + eq_(len(debug), 0) def test_conditions(self): m = Mapper() |