summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml17
-rw-r--r--routes/mapper.py12
-rw-r--r--routes/route.py25
-rw-r--r--routes/util.py4
-rw-r--r--setup.py6
-rw-r--r--tests/test_functional/test_explicit_use.py30
-rw-r--r--tests/test_units/test_route_escapes.py38
-rw-r--r--tox.ini10
8 files changed, 110 insertions, 32 deletions
diff --git a/.travis.yml b/.travis.yml
index a38f7f3..f9a351f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,21 +1,18 @@
language: python
python:
-- '2.6'
-- '2.7'
-- '3.3'
-- '3.4'
-- '3.5'
-- '3.6'
-- nightly
-- pypy
-- pypy3
+ - '2.7'
+ - '3.5'
+ - '3.6'
+ - nightly
+ - pypy
+ - pypy3
matrix:
allow_failures:
- python: nightly
install: pip install tox-travis
script: tox
after_success:
-- codecov
+ - codecov
deploy:
provider: pypi
user: bbangert
diff --git a/routes/mapper.py b/routes/mapper.py
index a819c7d..e88feef 100644
--- a/routes/mapper.py
+++ b/routes/mapper.py
@@ -490,7 +490,11 @@ class Mapper(SubMapperParent):
routepath = path_prefix + route.routepath
else:
routepath = route.routepath
- self.connect(route.name, routepath, **route._kargs)
+ self.connect(route.name,
+ routepath,
+ conditions=route.conditions,
+ **route._kargs
+ )
def make_route(self, *args, **kargs):
"""Make a new Route object
@@ -511,7 +515,7 @@ class Mapper(SubMapperParent):
m.connect('date/:year/:month/:day', controller="blog",
action="view")
m.connect('archives/:page', controller="blog", action="by_page",
- requirements = { 'page':'\d{1,2}' })
+ requirements = { 'page':'\\d{1,2}' })
m.connect('category_list', 'archives/category/:section',
controller='blog', action='category',
section='home', type='list')
@@ -995,7 +999,7 @@ class Mapper(SubMapperParent):
map.resource('message', 'messages',
path_prefix='{project_id}/',
- requirements={"project_id": R"\d+"})
+ requirements={"project_id": R"\\d+"})
# POST /01234/message
# success, project_id is set to "01234"
# POST /foo/message
@@ -1177,7 +1181,7 @@ class Mapper(SubMapperParent):
**route_options)
self.connect(name_prefix + name, path, **route_options)
- requirements_regexp = '[^\/]+(?<!\\\)'
+ requirements_regexp = '[^\\/]+(?<!\\\\)'
# Add the routes that deal with member methods of a resource
for method, lst in six.iteritems(member_methods):
diff --git a/routes/route.py b/routes/route.py
index 860a911..cf6df10 100644
--- a/routes/route.py
+++ b/routes/route.py
@@ -1,7 +1,5 @@
import re
import sys
-if sys.version < '2.4':
- from sets import ImmutableSet as frozenset
import six
from six.moves.urllib import parse as urlparse
@@ -37,9 +35,9 @@ class Route(object):
>>> newroute = Route(None, 'date/:year/:month/:day',
... controller="blog", action="view")
>>> newroute = Route(None, 'archives/:page', controller="blog",
- ... action="by_page", requirements = { 'page':'\d{1,2}' })
+ ... action="by_page", requirements = { 'page':'\\d{1,2}' })
>>> newroute.reqs
- {'page': '\\\d{1,2}'}
+ {'page': '\\\\d{1,2}'}
.. Note::
Route is generally not called directly, a Mapper instance
@@ -148,13 +146,22 @@ class Route(object):
"""Utility function to walk the route, and pull out the valid
dynamic/wildcard keys."""
collecting = False
+ escaping = False
current = ''
done_on = ''
var_type = ''
just_started = False
routelist = []
for char in routepath:
- if char in [':', '*', '{'] and not collecting and not self.static \
+ if escaping:
+ if char in ['\\', ':', '*', '{', '}']:
+ current += char
+ else:
+ current += '\\' + char
+ escaping = False
+ elif char == '\\':
+ escaping = True
+ elif char in [':', '*', '{'] and not collecting and not self.static \
or char in ['{'] and not collecting:
just_started = True
collecting = True
@@ -327,7 +334,7 @@ class Route(object):
else:
regpart = '(?:%s)' % partmatch
if part['type'] == '.':
- regparts.append('(?:\.%s)??' % regpart)
+ regparts.append(r'(?:\.%s)??' % regpart)
else:
regparts.append(regpart)
else:
@@ -370,7 +377,7 @@ class Route(object):
else:
partreg = '(?:%s)' % self.reqs[var]
if typ == '.':
- partreg = '(?:\.%s)??' % partreg
+ partreg = r'(?:\.%s)??' % partreg
elif var == 'controller':
if include_names:
partreg = '(?P<%s>%s)' % (var, '|'.join(map(re.escape,
@@ -393,7 +400,7 @@ class Route(object):
else:
partreg = '(?:[^%s]+?)' % exclude_chars
if typ == '.':
- partreg = '(?:\.%s)??' % partreg
+ partreg = r'(?:\.%s)??' % partreg
else:
end = ''.join(self.done_chars)
rem = rest
@@ -533,7 +540,7 @@ class Route(object):
if sub_domains and environ and 'HTTP_HOST' in environ:
host = environ['HTTP_HOST'].split(':')[0]
- sub_match = re.compile('^(.+?)\.%s$' % domain_match)
+ sub_match = re.compile(r'^(.+?)\.%s$' % domain_match)
subdomain = re.sub(sub_match, r'\1', host)
if subdomain not in sub_domains_ignore and host != subdomain:
sub_domain = subdomain
diff --git a/routes/util.py b/routes/util.py
index c48445f..54c8951 100644
--- a/routes/util.py
+++ b/routes/util.py
@@ -94,7 +94,7 @@ def _subdomain_check(kargs, mapper, environ):
if len(hostmatch) > 1:
port += ':' + hostmatch[1]
- match = re.match('^(.+?)\.(%s)$' % mapper.domain_match, host)
+ match = re.match(r'^(.+?)\.(%s)$' % mapper.domain_match, host)
host_subdomain, domain = match.groups() if match else (None, host)
subdomain = as_unicode(subdomain, mapper.encoding)
@@ -512,7 +512,7 @@ def controller_scan(directory=None):
for fname in os.listdir(dirname):
filename = os.path.join(dirname, fname)
if os.path.isfile(filename) and \
- re.match('^[^_]{1,1}.*\.py$', fname):
+ re.match(r'^[^_]{1,1}.*\.py$', fname):
controllers.append(prefix + fname[:-3])
elif os.path.isdir(filename):
controllers.extend(find_controllers(filename,
diff --git a/setup.py b/setup.py
index b0dd322..0b527da 100644
--- a/setup.py
+++ b/setup.py
@@ -43,12 +43,8 @@ setup(name="Routes",
"Programming Language :: Python :: Implementation :: CPython",
'Programming Language :: Python',
"Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.2",
- "Programming Language :: Python :: 3.3",
- "Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6"
],
@@ -60,7 +56,7 @@ setup(name="Routes",
test_suite="nose.collector",
include_package_data=True,
zip_safe=False,
- tests_require=['nose', 'webtest', 'webob', 'coverage'],
+ tests_require=["soupsieve<2.0", 'nose', 'webtest', 'webob', 'coverage'],
install_requires=[
"six",
"repoze.lru>=0.3"
diff --git a/tests/test_functional/test_explicit_use.py b/tests/test_functional/test_explicit_use.py
index ccd3b7a..b1e1cd7 100644
--- a/tests/test_functional/test_explicit_use.py
+++ b/tests/test_functional/test_explicit_use.py
@@ -1,6 +1,6 @@
"""test_explicit_use"""
import os, sys, time, unittest
-from nose.tools import eq_, assert_raises
+from nose.tools import eq_, assert_raises, assert_is_none
from routes import *
from routes.route import Route
@@ -101,6 +101,34 @@ class TestUtils(unittest.TestCase):
map.extend(routes)
eq_(map.match('/foo'), {})
+ def test_add_routes_conditions_unmet(self):
+ map = Mapper(explicit=True)
+ map.minimization = False
+ routes = [
+ Route('foo', '/foo', conditions=dict(method=["POST"]))
+ ]
+ environ = {
+ 'HTTP_HOST': 'localhost.com',
+ 'PATH_INFO': '/foo',
+ 'REQUEST_METHOD': 'GET',
+ }
+ map.extend(routes)
+ assert_is_none(map.match('/foo', environ=environ))
+
+ def test_add_routes_conditions_met(self):
+ map = Mapper(explicit=True)
+ map.minimization = False
+ routes = [
+ Route('foo', '/foo', conditions=dict(method=["POST"]))
+ ]
+ environ = {
+ 'HTTP_HOST': 'localhost.com',
+ 'PATH_INFO': '/foo',
+ 'REQUEST_METHOD': 'POST',
+ }
+ map.extend(routes)
+ eq_(map.match('/foo', environ=environ), {})
+
def test_using_func(self):
def fred(view):
pass
diff --git a/tests/test_units/test_route_escapes.py b/tests/test_units/test_route_escapes.py
new file mode 100644
index 0000000..5db07c4
--- /dev/null
+++ b/tests/test_units/test_route_escapes.py
@@ -0,0 +1,38 @@
+import unittest
+from routes.route import Route
+
+
+class TestRouteEscape(unittest.TestCase):
+ def test_normal_route(self):
+ r = Route('test', '/foo/bar')
+ self.assertEqual(r.routelist, ['/foo/bar'])
+
+ def test_route_with_backslash(self):
+ r = Route('test', '/foo\\\\bar')
+ self.assertEqual(r.routelist, ['/foo\\bar'])
+
+ def test_route_with_random_escapes(self):
+ r = Route('test', '\\/f\\oo\\/ba\\r')
+ self.assertEqual(r.routelist, ['\\/f\\oo\\/ba\\r'])
+
+ def test_route_with_colon(self):
+ r = Route('test', '/foo:bar/baz')
+ self.assertEqual(
+ r.routelist, ['/foo', {'name': 'bar', 'type': ':'}, '/', 'baz'])
+
+ def test_route_with_escaped_colon(self):
+ r = Route('test', '/foo\\:bar/baz')
+ self.assertEqual(r.routelist, ['/foo:bar/baz'])
+
+ def test_route_with_both_colons(self):
+ r = Route('test', '/prefix/escaped\\:escaped/foo=:notescaped/bar=42')
+ self.assertEqual(
+ r.routelist, ['/prefix/escaped:escaped/foo=',
+ {'name': 'notescaped', 'type': ':'}, '/', 'bar=42'])
+
+ def test_route_with_all_escapes(self):
+ r = Route('test', '/hmm\\:\\*\\{\\}*star/{brackets}/:colon')
+ self.assertEqual(
+ r.routelist, ['/hmm:*{}', {'name': 'star', 'type': '*'}, '/',
+ {'name': 'brackets', 'type': ':'}, '/',
+ {'name': 'colon', 'type': ':'}])
diff --git a/tox.ini b/tox.ini
index 0988735..4defb14 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py26,py27,py33,py34,py35,py36,pypy,pypy3
+envlist = py{27,35,36},pypy,pypy3,style
[testenv]
deps=
@@ -12,3 +12,11 @@ commands=
pip install .[middleware]
# webob optional dependency is fulfilled by [middleware] extra requirement
python -c "import webob"
+
+[testenv:style]
+deps = flake8
+commands = flake8 routes
+
+[flake8]
+# These are all ignored until someone decided to go fix them
+ignore = E125,E127,E128,E226,E305,E402,E501,E502,E504,F401,W503,W504