/usr/lib/python2.7/dist-packages/pyramid/urldispatch.py is in python-pyramid 1.6+dfsg-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 | import re
from zope.interface import implementer
from pyramid.interfaces import (
IRoutesMapper,
IRoute,
)
from pyramid.compat import (
PY3,
native_,
text_,
text_type,
string_types,
binary_type,
is_nonstr_iter,
decode_path_info,
)
from pyramid.exceptions import URLDecodeError
from pyramid.traversal import (
quote_path_segment,
split_path_info,
)
_marker = object()
@implementer(IRoute)
class Route(object):
def __init__(self, name, pattern, factory=None, predicates=(),
pregenerator=None):
self.pattern = pattern
self.path = pattern # indefinite b/w compat, not in interface
self.match, self.generate = _compile_route(pattern)
self.name = name
self.factory = factory
self.predicates = predicates
self.pregenerator = pregenerator
@implementer(IRoutesMapper)
class RoutesMapper(object):
def __init__(self):
self.routelist = []
self.static_routes = []
self.routes = {}
def has_routes(self):
return bool(self.routelist)
def get_routes(self, include_static=False):
if include_static is True:
return self.routelist + self.static_routes
return self.routelist
def get_route(self, name):
return self.routes.get(name)
def connect(self, name, pattern, factory=None, predicates=(),
pregenerator=None, static=False):
if name in self.routes:
oldroute = self.routes[name]
if oldroute in self.routelist:
self.routelist.remove(oldroute)
route = Route(name, pattern, factory, predicates, pregenerator)
if not static:
self.routelist.append(route)
else:
self.static_routes.append(route)
self.routes[name] = route
return route
def generate(self, name, kw):
return self.routes[name].generate(kw)
def __call__(self, request):
environ = request.environ
try:
# empty if mounted under a path in mod_wsgi, for example
path = decode_path_info(environ['PATH_INFO'] or '/')
except KeyError:
path = '/'
except UnicodeDecodeError as e:
raise URLDecodeError(e.encoding, e.object, e.start, e.end, e.reason)
for route in self.routelist:
match = route.match(path)
if match is not None:
preds = route.predicates
info = {'match':match, 'route':route}
if preds and not all((p(info, request) for p in preds)):
continue
return info
return {'route':None, 'match':None}
# stolen from bobo and modified
old_route_re = re.compile(r'(\:[_a-zA-Z]\w*)')
star_at_end = re.compile(r'\*(\w*)$')
# The tortuous nature of the regex named ``route_re`` below is due to the
# fact that we need to support at least one level of "inner" squigglies
# inside the expr of a {name:expr} pattern. This regex used to be just
# (\{[a-zA-Z][^\}]*\}) but that choked when supplied with e.g. {foo:\d{4}}.
route_re = re.compile(r'(\{[_a-zA-Z][^{}]*(?:\{[^{}]*\}[^{}]*)*\})')
def update_pattern(matchobj):
name = matchobj.group(0)
return '{%s}' % name[1:]
def _compile_route(route):
# This function really wants to consume Unicode patterns natively, but if
# someone passes us a bytestring, we allow it by converting it to Unicode
# using the ASCII decoding. We decode it using ASCII because we don't
# want to accept bytestrings with high-order characters in them here as
# we have no idea what the encoding represents.
if route.__class__ is not text_type:
try:
route = text_(route, 'ascii')
except UnicodeDecodeError:
raise ValueError(
'The pattern value passed to add_route must be '
'either a Unicode string or a plain string without '
'any non-ASCII characters (you provided %r).' % route)
if old_route_re.search(route) and not route_re.search(route):
route = old_route_re.sub(update_pattern, route)
if not route.startswith('/'):
route = '/' + route
remainder = None
if star_at_end.search(route):
route, remainder = route.rsplit('*', 1)
pat = route_re.split(route)
# every element in "pat" will be Unicode (regardless of whether the
# route_re regex pattern is itself Unicode or str)
pat.reverse()
rpat = []
gen = []
prefix = pat.pop() # invar: always at least one element (route='/'+route)
# We want to generate URL-encoded URLs, so we url-quote the prefix, being
# careful not to quote any embedded slashes. We have to replace '%' with
# '%%' afterwards, as the strings that go into "gen" are used as string
# replacement targets.
gen.append(quote_path_segment(prefix, safe='/').replace('%', '%%')) # native
rpat.append(re.escape(prefix)) # unicode
while pat:
name = pat.pop() # unicode
name = name[1:-1]
if ':' in name:
# reg may contain colons as well,
# so we must strictly split name into two parts
name, reg = name.split(':', 1)
else:
reg = '[^/]+'
gen.append('%%(%s)s' % native_(name)) # native
name = '(?P<%s>%s)' % (name, reg) # unicode
rpat.append(name)
s = pat.pop() # unicode
if s:
rpat.append(re.escape(s)) # unicode
# We want to generate URL-encoded URLs, so we url-quote this
# literal in the pattern, being careful not to quote the embedded
# slashes. We have to replace '%' with '%%' afterwards, as the
# strings that go into "gen" are used as string replacement
# targets. What is appended to gen is a native string.
gen.append(quote_path_segment(s, safe='/').replace('%', '%%'))
if remainder:
rpat.append('(?P<%s>.*?)' % remainder) # unicode
gen.append('%%(%s)s' % native_(remainder)) # native
pattern = ''.join(rpat) + '$' # unicode
match = re.compile(pattern).match
def matcher(path):
# This function really wants to consume Unicode patterns natively,
# but if someone passes us a bytestring, we allow it by converting it
# to Unicode using the ASCII decoding. We decode it using ASCII
# because we don't want to accept bytestrings with high-order
# characters in them here as we have no idea what the encoding
# represents.
if path.__class__ is not text_type:
path = text_(path, 'ascii')
m = match(path)
if m is None:
return None
d = {}
for k, v in m.groupdict().items():
# k and v will be Unicode 2.6.4 and lower doesnt accept unicode
# kwargs as **kw, so we explicitly cast the keys to native
# strings in case someone wants to pass the result as **kw
nk = native_(k, 'ascii')
if k == remainder:
d[nk] = split_path_info(v)
else:
d[nk] = v
return d
gen = ''.join(gen)
def generator(dict):
newdict = {}
for k, v in dict.items():
if PY3:
if v.__class__ is binary_type:
# url_quote below needs a native string, not bytes on Py3
v = v.decode('utf-8')
else:
if v.__class__ is text_type:
# url_quote below needs bytes, not unicode on Py2
v = v.encode('utf-8')
if k == remainder:
# a stararg argument
if is_nonstr_iter(v):
v = '/'.join(
[quote_path_segment(x, safe='/') for x in v]
) # native
else:
if v.__class__ not in string_types:
v = str(v)
v = quote_path_segment(v, safe='/')
else:
if v.__class__ not in string_types:
v = str(v)
# v may be bytes (py2) or native string (py3)
v = quote_path_segment(v, safe='/')
# at this point, the value will be a native string
newdict[k] = v
result = gen % newdict # native string result
return result
return matcher, generator
|