summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>2016-07-21 17:56:03 +0300
committerGitHub <noreply@github.com>2016-07-21 17:56:03 +0300
commit7c168b592ddfceb9e30b03aec0c9a60f5d600762 (patch)
tree1600a0928457d8bd3eab8a9ac8450f9ed021ec73
parent2ffd3b57370d9591cc6edb9a4df28f70eaf7d20d (diff)
parent61a49ad4994e1a7f10767c61b5a9186996140606 (diff)
downloadroutes-7c168b592ddfceb9e30b03aec0c9a60f5d600762.tar.gz
Merge pull request #3 from bbangert/master
Update from upstream #3
-rw-r--r--.travis.yml2
-rw-r--r--CHANGELOG.rst14
-rw-r--r--LICENSE.txt2
-rw-r--r--docs/conf.py6
-rw-r--r--routes/mapper.py22
-rw-r--r--routes/util.py44
-rw-r--r--setup.py4
-rw-r--r--tests/test_functional/test_explicit_use.py8
-rw-r--r--tests/test_functional/test_submapper.py6
-rw-r--r--tests/test_functional/test_utils.py2
10 files changed, 75 insertions, 35 deletions
diff --git a/.travis.yml b/.travis.yml
index 65a02f6..484b853 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,3 +13,5 @@ matrix:
- python: "nightly"
install: pip install tox-travis
script: tox
+after_success:
+ - codecov
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 2a8a322..bde71f9 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,8 +1,16 @@
Routes Changelog
%%%%%%%%%%%%%%%%
-Release 2.3 (**dev**)
-=====================
+Release 2.3.1 (March 30, 2016)
+==============================
+* Backwards compatability fix - connect should work with mandatory
+ routename and optional path. Patch by Davanum Srinivas (PR #65).
+
+Release 2.3 (March 28, 2016)
+============================
+* Fix sub_domain equivalence check. Patch by Nikita Uvarov
+* Add support for protocol-relative URLs generation (i.e. starting with double
+ slash ``//``). PR #60. Patch by Sviatoslav Sydorenko.
* Add support for the ``middleware`` extra requirement, making possible to
depend on ``webob`` optionally. PR #59. Patch by Sviatoslav Sydorenko.
* Fix matching of an empty string route, which led to exception in earlier
@@ -431,7 +439,7 @@ Release 1.0 (Nov. 21st, 2005)
Or::
- from routes import request_confg, Mapper
+ from routes import request_config, Mapper
The following names are available for importing from routes::
diff --git a/LICENSE.txt b/LICENSE.txt
index 493ea52..5a11de7 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2005-2015 Ben Bangert <ben@groovie.org>
+Copyright (c) 2005-2016 Ben Bangert <ben@groovie.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/docs/conf.py b/docs/conf.py
index 6673a37..467a6be 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -36,15 +36,15 @@ master_doc = 'index'
# General substitutions.
project = 'Routes'
-copyright = '2005-2015, Ben Bangert, Mike Orr'
+copyright = '2005-2016, Ben Bangert, Mike Orr, and numerous contributers'
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.13'
+version = '2.3'
# The full version, including alpha/beta/rc tags.
-release = '1.13'
+release = '2.3.1'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
diff --git a/routes/mapper.py b/routes/mapper.py
index 06f09f2..eb4060a 100644
--- a/routes/mapper.py
+++ b/routes/mapper.py
@@ -162,25 +162,25 @@ class SubMapper(SubMapperParent):
self.formatted = True
self.add_actions(actions or [], **kwargs)
- def connect(self, *args, **kwargs):
+ def connect(self, routename, path=None, **kwargs):
newkargs = {}
- # newargs = args
- routename, path = args
+ _routename = routename
+ _path = path
for key, value in six.iteritems(self.kwargs):
if key == 'path_prefix':
- if len(args) > 1:
+ if path is not None:
# if there's a name_prefix, add it to the route name
# and if there's a path_prefix
- path = ''.join((self.kwargs[key], args[1]))
+ _path = ''.join((self.kwargs[key], path))
else:
- path = ''.join((self.kwargs[key], args[0]))
+ _path = ''.join((self.kwargs[key], routename))
elif key == 'name_prefix':
- if len(args) > 1:
+ if path is not None:
# if there's a name_prefix, add it to the route name
# and if there's a path_prefix
- routename = ''.join((self.kwargs[key], args[0]))
+ _routename = ''.join((self.kwargs[key], routename))
else:
- routename = None
+ _routename = None
elif key in kwargs:
if isinstance(value, dict):
newkargs[key] = dict(value, **kwargs[key]) # merge dicts
@@ -197,7 +197,7 @@ class SubMapper(SubMapperParent):
if key not in self.kwargs:
newkargs[key] = kwargs[key]
- newargs = (routename, path)
+ newargs = (_routename, _path)
return self.obj.connect(*newargs, **newkargs)
def link(self, rel=None, name=None, action=None, method='GET',
@@ -1230,7 +1230,7 @@ class Mapper(SubMapperParent):
Example::
map = Mapper()
- map.redirect('/legacyapp/archives/{url:.*}, '/archives/{url})
+ map.redirect('/legacyapp/archives/{url:.*}', '/archives/{url}')
map.redirect('/home/index', '/',
_redirect_code='301 Moved Permanently')
diff --git a/routes/util.py b/routes/util.py
index baeeac7..c48445f 100644
--- a/routes/util.py
+++ b/routes/util.py
@@ -93,10 +93,12 @@ def _subdomain_check(kargs, mapper, environ):
port = ''
if len(hostmatch) > 1:
port += ':' + hostmatch[1]
- sub_match = re.compile('^.+?\.(%s)$' % mapper.domain_match)
- domain = re.sub(sub_match, r'\1', host)
+
+ match = re.match('^(.+?)\.(%s)$' % mapper.domain_match, host)
+ host_subdomain, domain = match.groups() if match else (None, host)
+
subdomain = as_unicode(subdomain, mapper.encoding)
- if subdomain and not host.startswith(subdomain) and \
+ if subdomain and host_subdomain != subdomain and \
subdomain not in mapper.sub_domains_ignore:
kargs['_host'] = subdomain + '.' + domain + port
elif (subdomain in mapper.sub_domains_ignore or \
@@ -180,15 +182,19 @@ def url_for(*args, **kargs):
"""
anchor = kargs.get('anchor')
host = kargs.get('host')
- protocol = kargs.get('protocol')
+ protocol = kargs.pop('protocol', None)
qualified = kargs.pop('qualified', None)
# Remove special words from kargs, convert placeholders
- for key in ['anchor', 'host', 'protocol']:
+ for key in ['anchor', 'host']:
if kargs.get(key):
del kargs[key]
if key+'_' in kargs:
kargs[key] = kargs.pop(key+'_')
+
+ if 'protocol_' in kargs:
+ kargs['protocol_'] = protocol
+
config = request_config()
route = None
static = False
@@ -250,21 +256,23 @@ def url_for(*args, **kargs):
newargs = _screenargs(kargs, config.mapper, environ)
anchor = newargs.pop('_anchor', None) or anchor
host = newargs.pop('_host', None) or host
- protocol = newargs.pop('_protocol', None) or protocol
+ protocol = newargs.pop('_protocol', protocol)
url = config.mapper.generate(*route_args, **newargs)
if anchor is not None:
url += '#' + _url_quote(anchor, encoding)
- if host or protocol or qualified:
+ if host or (protocol is not None) or qualified:
if not host and not qualified:
# Ensure we don't use a specific port, as changing the protocol
# means that we most likely need a new port
host = config.host.split(':')[0]
elif not host:
host = config.host
- if not protocol:
+ if protocol is None:
protocol = config.protocol
+ if protocol != '':
+ protocol += ':'
if url is not None:
- url = protocol + '://' + host + url
+ url = protocol + '//' + host + url
if not ascii_characters(url) and url is not None:
raise GenerationException("url_for can only return a string, got "
@@ -324,16 +332,19 @@ class URLGenerator(object):
"""
anchor = kargs.get('anchor')
host = kargs.get('host')
- protocol = kargs.get('protocol')
+ protocol = kargs.pop('protocol', None)
qualified = kargs.pop('qualified', None)
# Remove special words from kargs, convert placeholders
- for key in ['anchor', 'host', 'protocol']:
+ for key in ['anchor', 'host']:
if kargs.get(key):
del kargs[key]
if key+'_' in kargs:
kargs[key] = kargs.pop(key+'_')
+ if 'protocol_' in kargs:
+ kargs['protocol_'] = protocol
+
route = None
use_current = '_use_current' in kargs and kargs.pop('_use_current')
@@ -396,12 +407,13 @@ class URLGenerator(object):
anchor = anchor or newargs.pop('_anchor', None)
host = host or newargs.pop('_host', None)
- protocol = protocol or newargs.pop('_protocol', None)
+ if protocol is None:
+ protocol = newargs.pop('_protocol', None)
newargs['_environ'] = self.environ
url = self.mapper.generate(*route_args, **newargs)
if anchor is not None:
url += '#' + _url_quote(anchor, encoding)
- if host or protocol or qualified:
+ if host or (protocol is not None) or qualified:
if 'routes.cached_hostinfo' not in self.environ:
cache_hostinfo(self.environ)
hostinfo = self.environ['routes.cached_hostinfo']
@@ -412,12 +424,14 @@ class URLGenerator(object):
host = hostinfo['host'].split(':')[0]
elif not host:
host = hostinfo['host']
- if not protocol:
+ if protocol is None:
protocol = hostinfo['protocol']
+ if protocol != '':
+ protocol += ':'
if url is not None:
if host[-1] != '/':
host += '/'
- url = protocol + '://' + host + url.lstrip('/')
+ url = protocol + '//' + host + url.lstrip('/')
if not ascii_characters(url) and url is not None:
raise GenerationException("Can only return a string, got "
diff --git a/setup.py b/setup.py
index c3be076..989c01d 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-__version__ = '2.2'
+__version__ = '2.3.1'
import io
import os
@@ -47,7 +47,7 @@ setup(name="Routes",
keywords='routes webob dispatch',
author="Ben Bangert",
author_email="ben@groovie.org",
- url='http://routes.readthedocs.org/',
+ url='https://routes.readthedocs.io/',
license="MIT",
test_suite="nose.collector",
include_package_data=True,
diff --git a/tests/test_functional/test_explicit_use.py b/tests/test_functional/test_explicit_use.py
index 32579d8..ccd3b7a 100644
--- a/tests/test_functional/test_explicit_use.py
+++ b/tests/test_functional/test_explicit_use.py
@@ -52,6 +52,14 @@ class TestUtils(unittest.TestCase):
url = URLGenerator(m, environ.copy())
assert_raises(GenerationException, lambda: url.current(qualified=True))
+ environ = {'HTTP_HOST': 'subdomain.localhost.com'}
+ url = URLGenerator(m, environ.copy())
+ eq_('http://sub.localhost.com/hi/smith', url(fred='smith', sub_domain='sub', qualified=True))
+
+ environ = {'HTTP_HOST': 'sub.sub.localhost.com'}
+ url = URLGenerator(m, environ.copy())
+ eq_('http://new.localhost.com/hi/smith', url(fred='smith', sub_domain='new', qualified=True))
+
url = URLGenerator(m, {})
eq_('/hi/smith', url(fred='smith', sub_domain=u'home'))
diff --git a/tests/test_functional/test_submapper.py b/tests/test_functional/test_submapper.py
index 1516a58..8821de9 100644
--- a/tests/test_functional/test_submapper.py
+++ b/tests/test_functional/test_submapper.py
@@ -13,6 +13,12 @@ class TestSubmapper(unittest.TestCase):
eq_('/entries/1', url_for('entry', id=1))
assert_raises(Exception, url_for, 'entry', id='foo')
+ def test_submapper_with_no_path(self):
+ m = Mapper()
+ c = m.submapper(path_prefix='/')
+ c.connect('entry')
+ eq_('/entry?id=1', url_for('entry', id=1))
+
def test_submapper_nesting(self):
m = Mapper()
c = m.submapper(path_prefix='/entries', controller='entry',
diff --git a/tests/test_functional/test_utils.py b/tests/test_functional/test_utils.py
index 3f4dd47..e2a6a04 100644
--- a/tests/test_functional/test_utils.py
+++ b/tests/test_functional/test_utils.py
@@ -62,6 +62,8 @@ class TestUtils(unittest.TestCase):
eq_('/content', urlobj())
eq_('https://www.test.com/viewpost', urlobj(controller='post', action='view', protocol='https'))
eq_('http://www.test.org/content', urlobj(host='www.test.org'))
+ eq_('//www.test.com/viewpost', urlobj(controller='post', action='view', protocol=''))
+ eq_('//www.test.org/content', urlobj(host='www.test.org', protocol=''))
def test_url_raises(self):
con = self.con