summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG5
-rw-r--r--routes/mapper.py22
-rw-r--r--routes/route.py73
-rw-r--r--tests/test_functional/test_recognition.py4
4 files changed, 82 insertions, 22 deletions
diff --git a/CHANGELOG b/CHANGELOG
index ca86bff..c925899 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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()