commit | author | age
|
efd61e
|
1 |
import contextlib |
b01f1d
|
2 |
import warnings |
CM |
3 |
|
84367e
|
4 |
from pyramid.compat import urlparse |
ee117e
|
5 |
from pyramid.interfaces import ( |
CM |
6 |
IRequest, |
|
7 |
IRouteRequest, |
|
8 |
IRoutesMapper, |
|
9 |
PHASE2_CONFIG, |
|
10 |
) |
5bf23f
|
11 |
|
CM |
12 |
from pyramid.exceptions import ConfigurationError |
|
13 |
from pyramid.request import route_request_iface |
|
14 |
from pyramid.urldispatch import RoutesMapper |
|
15 |
|
30f79d
|
16 |
from pyramid.util import ( |
MM |
17 |
as_sorted_tuple, |
|
18 |
is_nonstr_iter, |
|
19 |
) |
9c8ec5
|
20 |
|
c7974f
|
21 |
import pyramid.predicates |
5bf23f
|
22 |
|
52fde9
|
23 |
from pyramid.config.util import ( |
MM |
24 |
action_method, |
4a9f4f
|
25 |
normalize_accept_offer, |
52fde9
|
26 |
predvalseq, |
MM |
27 |
) |
|
28 |
|
5bf23f
|
29 |
class RoutesConfiguratorMixin(object): |
CM |
30 |
@action_method |
|
31 |
def add_route(self, |
|
32 |
name, |
|
33 |
pattern=None, |
|
34 |
factory=None, |
|
35 |
for_=None, |
|
36 |
header=None, |
9c8ec5
|
37 |
xhr=None, |
5bf23f
|
38 |
accept=None, |
CM |
39 |
path_info=None, |
|
40 |
request_method=None, |
|
41 |
request_param=None, |
|
42 |
traverse=None, |
|
43 |
custom_predicates=(), |
|
44 |
use_global_views=False, |
|
45 |
path=None, |
|
46 |
pregenerator=None, |
|
47 |
static=False, |
8ec8e2
|
48 |
**predicates): |
5bf23f
|
49 |
""" Add a :term:`route configuration` to the current |
CM |
50 |
configuration state, as well as possibly a :term:`view |
|
51 |
configuration` to be used to specify a :term:`view callable` |
|
52 |
that will be invoked when this route matches. The arguments |
|
53 |
to this method are divided into *predicate*, *non-predicate*, |
|
54 |
and *view-related* types. :term:`Route predicate` arguments |
|
55 |
narrow the circumstances in which a route will be match a |
|
56 |
request; non-predicate arguments are informational. |
|
57 |
|
|
58 |
Non-Predicate Arguments |
|
59 |
|
|
60 |
name |
|
61 |
|
|
62 |
The name of the route, e.g. ``myroute``. This attribute is |
|
63 |
required. It must be unique among all defined routes in a given |
|
64 |
application. |
|
65 |
|
|
66 |
factory |
|
67 |
|
|
68 |
A Python object (often a function or a class) or a :term:`dotted |
|
69 |
Python name` which refers to the same object that will generate a |
|
70 |
:app:`Pyramid` root resource object when this route matches. For |
|
71 |
example, ``mypackage.resources.MyFactory``. If this argument is |
0507ac
|
72 |
not specified, a default root factory will be used. See |
CM |
73 |
:ref:`the_resource_tree` for more information about root factories. |
5bf23f
|
74 |
|
CM |
75 |
traverse |
|
76 |
|
|
77 |
If you would like to cause the :term:`context` to be |
|
78 |
something other than the :term:`root` object when this route |
|
79 |
matches, you can spell a traversal pattern as the |
|
80 |
``traverse`` argument. This traversal pattern will be used |
|
81 |
as the traversal path: traversal will begin at the root |
|
82 |
object implied by this route (either the global root, or the |
|
83 |
object returned by the ``factory`` associated with this |
|
84 |
route). |
|
85 |
|
|
86 |
The syntax of the ``traverse`` argument is the same as it is |
|
87 |
for ``pattern``. For example, if the ``pattern`` provided to |
|
88 |
``add_route`` is ``articles/{article}/edit``, and the |
|
89 |
``traverse`` argument provided to ``add_route`` is |
|
90 |
``/{article}``, when a request comes in that causes the route |
|
91 |
to match in such a way that the ``article`` match value is |
f1f49b
|
92 |
``'1'`` (when the request URI is ``/articles/1/edit``), the |
5bf23f
|
93 |
traversal path will be generated as ``/1``. This means that |
CM |
94 |
the root object's ``__getitem__`` will be called with the |
f1f49b
|
95 |
name ``'1'`` during the traversal phase. If the ``'1'`` object |
5bf23f
|
96 |
exists, it will become the :term:`context` of the request. |
CM |
97 |
:ref:`traversal_chapter` has more information about |
|
98 |
traversal. |
|
99 |
|
|
100 |
If the traversal path contains segment marker names which |
|
101 |
are not present in the ``pattern`` argument, a runtime error |
|
102 |
will occur. The ``traverse`` pattern should not contain |
|
103 |
segment markers that do not exist in the ``pattern`` |
|
104 |
argument. |
|
105 |
|
|
106 |
A similar combining of routing and traversal is available |
|
107 |
when a route is matched which contains a ``*traverse`` |
|
108 |
remainder marker in its pattern (see |
|
109 |
:ref:`using_traverse_in_a_route_pattern`). The ``traverse`` |
|
110 |
argument to add_route allows you to associate route patterns |
043ccd
|
111 |
with an arbitrary traversal path without using a |
5bf23f
|
112 |
``*traverse`` remainder marker; instead you can use other |
CM |
113 |
match information. |
|
114 |
|
|
115 |
Note that the ``traverse`` argument to ``add_route`` is |
|
116 |
ignored when attached to a route that has a ``*traverse`` |
|
117 |
remainder marker in its pattern. |
|
118 |
|
|
119 |
pregenerator |
|
120 |
|
|
121 |
This option should be a callable object that implements the |
|
122 |
:class:`pyramid.interfaces.IRoutePregenerator` interface. A |
|
123 |
:term:`pregenerator` is a callable called by the |
|
124 |
:meth:`pyramid.request.Request.route_url` function to augment or |
|
125 |
replace the arguments it is passed when generating a URL for the |
|
126 |
route. This is a feature not often used directly by applications, |
|
127 |
it is meant to be hooked by frameworks that use :app:`Pyramid` as |
|
128 |
a base. |
|
129 |
|
|
130 |
use_global_views |
|
131 |
|
|
132 |
When a request matches this route, and view lookup cannot |
|
133 |
find a view which has a ``route_name`` predicate argument |
|
134 |
that matches the route, try to fall back to using a view |
|
135 |
that otherwise matches the context, request, and view name |
|
136 |
(but which does not match the route_name predicate). |
|
137 |
|
|
138 |
static |
|
139 |
|
|
140 |
If ``static`` is ``True``, this route will never match an incoming |
|
141 |
request; it will only be useful for URL generation. By default, |
|
142 |
``static`` is ``False``. See :ref:`static_route_narr`. |
|
143 |
|
0b23b3
|
144 |
.. versionadded:: 1.1 |
5bf23f
|
145 |
|
CM |
146 |
Predicate Arguments |
|
147 |
|
|
148 |
pattern |
|
149 |
|
|
150 |
The pattern of the route e.g. ``ideas/{idea}``. This |
|
151 |
argument is required. See :ref:`route_pattern_syntax` |
|
152 |
for information about the syntax of route patterns. If the |
|
153 |
pattern doesn't match the current URL, route matching |
|
154 |
continues. |
|
155 |
|
012b97
|
156 |
.. note:: |
M |
157 |
|
|
158 |
For backwards compatibility purposes (as of :app:`Pyramid` 1.0), a |
|
159 |
``path`` keyword argument passed to this function will be used to |
|
160 |
represent the pattern value if the ``pattern`` argument is |
|
161 |
``None``. If both ``path`` and ``pattern`` are passed, ``pattern`` |
|
162 |
wins. |
|
163 |
|
5bf23f
|
164 |
xhr |
CM |
165 |
|
|
166 |
This value should be either ``True`` or ``False``. If this |
|
167 |
value is specified and is ``True``, the :term:`request` must |
|
168 |
possess an ``HTTP_X_REQUESTED_WITH`` (aka |
|
169 |
``X-Requested-With``) header for this route to match. This |
|
170 |
is useful for detecting AJAX requests issued from jQuery, |
|
171 |
Prototype and other Javascript libraries. If this predicate |
|
172 |
returns ``False``, route matching continues. |
|
173 |
|
|
174 |
request_method |
|
175 |
|
49f082
|
176 |
A string representing an HTTP method name, e.g. ``GET``, ``POST``, |
CM |
177 |
``HEAD``, ``DELETE``, ``PUT`` or a tuple of elements containing |
|
178 |
HTTP method names. If this argument is not specified, this route |
|
179 |
will match if the request has *any* request method. If this |
|
180 |
predicate returns ``False``, route matching continues. |
|
181 |
|
0b23b3
|
182 |
.. versionchanged:: 1.2 |
TL |
183 |
The ability to pass a tuple of items as ``request_method``. |
|
184 |
Previous versions allowed only a string. |
5bf23f
|
185 |
|
CM |
186 |
path_info |
|
187 |
|
|
188 |
This value represents a regular expression pattern that will |
|
189 |
be tested against the ``PATH_INFO`` WSGI environment |
|
190 |
variable. If the regex matches, this predicate will return |
|
191 |
``True``. If this predicate returns ``False``, route |
|
192 |
matching continues. |
|
193 |
|
|
194 |
request_param |
|
195 |
|
939165
|
196 |
This value can be any string or an iterable of strings. A view |
BG |
197 |
declaration with this argument ensures that the associated route will |
|
198 |
only match when the request has a key in the ``request.params`` |
5bf23f
|
199 |
dictionary (an HTTP ``GET`` or ``POST`` variable) that has a |
CM |
200 |
name which matches the supplied value. If the value |
|
201 |
supplied as the argument has a ``=`` sign in it, |
|
202 |
e.g. ``request_param="foo=123"``, then the key |
|
203 |
(``foo``) must both exist in the ``request.params`` dictionary, and |
|
204 |
the value must match the right hand side of the expression (``123``) |
|
205 |
for the route to "match" the current request. If this predicate |
|
206 |
returns ``False``, route matching continues. |
|
207 |
|
|
208 |
header |
|
209 |
|
|
210 |
This argument represents an HTTP header name or a header |
|
211 |
name/value pair. If the argument contains a ``:`` (colon), |
|
212 |
it will be considered a name/value pair |
|
213 |
(e.g. ``User-Agent:Mozilla/.*`` or ``Host:localhost``). If |
|
214 |
the value contains a colon, the value portion should be a |
|
215 |
regular expression. If the value does not contain a colon, |
|
216 |
the entire value will be considered to be the header name |
|
217 |
(e.g. ``If-Modified-Since``). If the value evaluates to a |
|
218 |
header name only without a value, the header specified by |
|
219 |
the name must be present in the request for this predicate |
|
220 |
to be true. If the value evaluates to a header name/value |
|
221 |
pair, the header specified by the name must be present in |
|
222 |
the request *and* the regular expression specified as the |
|
223 |
value must match the header value. Whether or not the value |
|
224 |
represents a header name or a header name/value pair, the |
|
225 |
case of the header name is not significant. If this |
|
226 |
predicate returns ``False``, route matching continues. |
|
227 |
|
121f45
|
228 |
accept |
MM |
229 |
|
30f79d
|
230 |
A :term:`media type` that will be matched against the ``Accept`` |
MM |
231 |
HTTP request header. This value may be a specific media type such |
4a9f4f
|
232 |
as ``text/html``, or a list of the same. If the media type is |
MM |
233 |
acceptable by the ``Accept`` header of the request, or if the |
|
234 |
``Accept`` header isn't set at all in the request, this predicate |
|
235 |
will match. If this does not match the ``Accept`` header of the |
|
236 |
request, route matching continues. |
121f45
|
237 |
|
MM |
238 |
If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is |
|
239 |
not taken into consideration when deciding whether or not to select |
|
240 |
the route. |
4a9f4f
|
241 |
|
MM |
242 |
.. versionchanged:: 1.10 |
|
243 |
|
|
244 |
Specifying a media range is deprecated due to changes in WebOb |
|
245 |
and ambiguities that occur when trying to match ranges against |
|
246 |
ranges in the ``Accept`` header. Support will be removed in |
|
247 |
:app:`Pyramid` 2.0. Use a list of specific media types to match |
|
248 |
more than one type. |
121f45
|
249 |
|
c7337b
|
250 |
effective_principals |
CM |
251 |
|
|
252 |
If specified, this value should be a :term:`principal` identifier or |
|
253 |
a sequence of principal identifiers. If the |
0184b5
|
254 |
:attr:`pyramid.request.Request.effective_principals` property |
CM |
255 |
indicates that every principal named in the argument list is present |
|
256 |
in the current request, this predicate will return True; otherwise it |
|
257 |
will return False. For example: |
c7337b
|
258 |
``effective_principals=pyramid.security.Authenticated`` or |
CM |
259 |
``effective_principals=('fred', 'group:admins')``. |
|
260 |
|
|
261 |
.. versionadded:: 1.4a4 |
|
262 |
|
5bf23f
|
263 |
custom_predicates |
CM |
264 |
|
e96f1b
|
265 |
.. deprecated:: 1.5 |
2033ee
|
266 |
This value should be a sequence of references to custom |
SP |
267 |
predicate callables. Use custom predicates when no set of |
|
268 |
predefined predicates does what you need. Custom predicates |
|
269 |
can be combined with predefined predicates as necessary. |
|
270 |
Each custom predicate callable should accept two arguments: |
|
271 |
``info`` and ``request`` and should return either ``True`` |
|
272 |
or ``False`` after doing arbitrary evaluation of the info |
|
273 |
and/or the request. If all custom and non-custom predicate |
|
274 |
callables return ``True`` the associated route will be |
|
275 |
considered viable for a given request. If any predicate |
|
276 |
callable returns ``False``, route matching continues. Note |
|
277 |
that the value ``info`` passed to a custom route predicate |
|
278 |
is a dictionary containing matching information; see |
|
279 |
:ref:`custom_route_predicates` for more information about |
|
280 |
``info``. |
5bf23f
|
281 |
|
8ec8e2
|
282 |
predicates |
9c8ec5
|
283 |
|
5664c4
|
284 |
Pass a key/value pair here to use a third-party predicate |
CM |
285 |
registered via |
cfd8cc
|
286 |
:meth:`pyramid.config.Configurator.add_route_predicate`. More than |
5664c4
|
287 |
one key/value pair can be used at the same time. See |
95f766
|
288 |
:ref:`view_and_route_predicates` for more information about |
0b23b3
|
289 |
third-party predicates. |
TL |
290 |
|
|
291 |
.. versionadded:: 1.4 |
|
292 |
|
5bf23f
|
293 |
""" |
b01f1d
|
294 |
if custom_predicates: |
CM |
295 |
warnings.warn( |
|
296 |
('The "custom_predicates" argument to Configurator.add_route ' |
|
297 |
'is deprecated as of Pyramid 1.5. Use ' |
|
298 |
'"config.add_route_predicate" and use the registered ' |
|
299 |
'route predicate as a predicate argument to add_route ' |
|
300 |
'instead. See "Adding A Third Party View, Route, or ' |
|
301 |
'Subscriber Predicate" in the "Hooks" chapter of the ' |
|
302 |
'documentation for more information.'), |
c151ad
|
303 |
DeprecationWarning, |
b01f1d
|
304 |
stacklevel=3 |
CM |
305 |
) |
121f45
|
306 |
|
MM |
307 |
if accept is not None: |
c3c83e
|
308 |
if not is_nonstr_iter(accept): |
4a9f4f
|
309 |
if '*' in accept: |
MM |
310 |
warnings.warn( |
|
311 |
('Passing a media range to the "accept" argument of ' |
|
312 |
'Configurator.add_route is deprecated as of Pyramid ' |
|
313 |
'1.10. Use a list of explicit media types.'), |
|
314 |
DeprecationWarning, |
|
315 |
stacklevel=3, |
|
316 |
) |
|
317 |
# XXX switch this to verify=True when range support is dropped |
|
318 |
accept = [normalize_accept_offer(accept, verify=False)] |
30f79d
|
319 |
|
4a9f4f
|
320 |
else: |
MM |
321 |
accept = [ |
|
322 |
normalize_accept_offer(accept_option) |
|
323 |
for accept_option in accept |
|
324 |
] |
121f45
|
325 |
|
5bf23f
|
326 |
# these are route predicates; if they do not match, the next route |
CM |
327 |
# in the routelist will be tried |
d8e504
|
328 |
if request_method is not None: |
CM |
329 |
request_method = as_sorted_tuple(request_method) |
|
330 |
|
5bf23f
|
331 |
factory = self.maybe_dotted(factory) |
CM |
332 |
if pattern is None: |
|
333 |
pattern = path |
|
334 |
if pattern is None: |
|
335 |
raise ConfigurationError('"pattern" argument may not be None') |
|
336 |
|
d07d16
|
337 |
# check for an external route; an external route is one which is |
CM |
338 |
# is a full url (e.g. 'http://example.com/{id}') |
84367e
|
339 |
parsed = urlparse.urlparse(pattern) |
582c2e
|
340 |
external_url = pattern |
JA |
341 |
|
84367e
|
342 |
if parsed.hostname: |
MM |
343 |
pattern = parsed.path |
|
344 |
|
|
345 |
original_pregenerator = pregenerator |
|
346 |
def external_url_pregenerator(request, elements, kw): |
d07d16
|
347 |
if '_app_url' in kw: |
CM |
348 |
raise ValueError( |
|
349 |
'You cannot generate a path to an external route ' |
|
350 |
'pattern via request.route_path nor pass an _app_url ' |
|
351 |
'to request.route_url when generating a URL for an ' |
|
352 |
'external route pattern (pattern was "%s") ' % |
|
353 |
(pattern,) |
|
354 |
) |
|
355 |
if '_scheme' in kw: |
|
356 |
scheme = kw['_scheme'] |
|
357 |
elif parsed.scheme: |
|
358 |
scheme = parsed.scheme |
|
359 |
else: |
|
360 |
scheme = request.scheme |
|
361 |
kw['_app_url'] = '{0}://{1}'.format(scheme, parsed.netloc) |
84367e
|
362 |
|
MM |
363 |
if original_pregenerator: |
|
364 |
elements, kw = original_pregenerator( |
|
365 |
request, elements, kw) |
|
366 |
return elements, kw |
|
367 |
|
|
368 |
pregenerator = external_url_pregenerator |
|
369 |
static = True |
|
370 |
|
|
371 |
elif self.route_prefix: |
5bf23f
|
372 |
pattern = self.route_prefix.rstrip('/') + '/' + pattern.lstrip('/') |
33b638
|
373 |
|
eb2fee
|
374 |
mapper = self.get_routes_mapper() |
5bf23f
|
375 |
|
522405
|
376 |
introspectables = [] |
CM |
377 |
|
|
378 |
intr = self.introspectable('routes', |
|
379 |
name, |
87f8d2
|
380 |
'%s (pattern: %r)' % (name, pattern), |
CM |
381 |
'route') |
3b5ccb
|
382 |
intr['name'] = name |
CM |
383 |
intr['pattern'] = pattern |
|
384 |
intr['factory'] = factory |
87f8d2
|
385 |
intr['xhr'] = xhr |
d8e504
|
386 |
intr['request_methods'] = request_method |
87f8d2
|
387 |
intr['path_info'] = path_info |
CM |
388 |
intr['request_param'] = request_param |
|
389 |
intr['header'] = header |
|
390 |
intr['accept'] = accept |
|
391 |
intr['traverse'] = traverse |
|
392 |
intr['custom_predicates'] = custom_predicates |
3b5ccb
|
393 |
intr['pregenerator'] = pregenerator |
CM |
394 |
intr['static'] = static |
|
395 |
intr['use_global_views'] = use_global_views |
582c2e
|
396 |
|
JA |
397 |
if static is True: |
|
398 |
intr['external_url'] = external_url |
|
399 |
|
522405
|
400 |
introspectables.append(intr) |
CM |
401 |
|
|
402 |
if factory: |
|
403 |
factory_intr = self.introspectable('root factories', |
|
404 |
name, |
|
405 |
self.object_description(factory), |
|
406 |
'root factory') |
|
407 |
factory_intr['factory'] = factory |
|
408 |
factory_intr['route_name'] = name |
|
409 |
factory_intr.relate('routes', name) |
|
410 |
introspectables.append(factory_intr) |
3b5ccb
|
411 |
|
de79bc
|
412 |
def register_route_request_iface(): |
b9f2f5
|
413 |
request_iface = self.registry.queryUtility(IRouteRequest, name=name) |
MM |
414 |
if request_iface is None: |
|
415 |
if use_global_views: |
|
416 |
bases = (IRequest,) |
|
417 |
else: |
|
418 |
bases = () |
|
419 |
request_iface = route_request_iface(name, bases) |
|
420 |
self.registry.registerUtility( |
|
421 |
request_iface, IRouteRequest, name=name) |
|
422 |
|
de79bc
|
423 |
def register_connect(): |
8ec8e2
|
424 |
pvals = predicates.copy() |
9c8ec5
|
425 |
pvals.update( |
CM |
426 |
dict( |
|
427 |
xhr=xhr, |
|
428 |
request_method=request_method, |
|
429 |
path_info=path_info, |
|
430 |
request_param=request_param, |
|
431 |
header=header, |
|
432 |
accept=accept, |
|
433 |
traverse=traverse, |
|
434 |
custom=predvalseq(custom_predicates), |
|
435 |
) |
|
436 |
) |
|
437 |
|
405213
|
438 |
predlist = self.get_predlist('route') |
9c8ec5
|
439 |
_, preds, _ = predlist.make(self, **pvals) |
3b5ccb
|
440 |
route = mapper.connect( |
9c8ec5
|
441 |
name, pattern, factory, predicates=preds, |
3b5ccb
|
442 |
pregenerator=pregenerator, static=static |
CM |
443 |
) |
|
444 |
intr['object'] = route |
|
445 |
return route |
eb2fee
|
446 |
|
de79bc
|
447 |
# We have to connect routes in the order they were provided; |
CM |
448 |
# we can't use a phase to do that, because when the actions are |
|
449 |
# sorted, actions in the same phase lose relative ordering |
19a575
|
450 |
self.action(('route-connect', name), register_connect) |
de79bc
|
451 |
|
CM |
452 |
# But IRouteRequest interfaces must be registered before we begin to |
|
453 |
# process view registrations (in phase 3) |
|
454 |
self.action(('route', name), register_route_request_iface, |
522405
|
455 |
order=PHASE2_CONFIG, introspectables=introspectables) |
381de3
|
456 |
|
9c8ec5
|
457 |
@action_method |
CM |
458 |
def add_route_predicate(self, name, factory, weighs_more_than=None, |
cfd8cc
|
459 |
weighs_less_than=None): |
9c8ec5
|
460 |
""" Adds a route predicate factory. The view predicate can later be |
CM |
461 |
named as a keyword argument to |
|
462 |
:meth:`pyramid.config.Configurator.add_route`. |
|
463 |
|
|
464 |
``name`` should be the name of the predicate. It must be a valid |
|
465 |
Python identifier (it will be used as a keyword argument to |
cfd8cc
|
466 |
``add_route``). |
9c8ec5
|
467 |
|
d71aca
|
468 |
``factory`` should be a :term:`predicate factory` or :term:`dotted |
BJR |
469 |
Python name` which refers to a predicate factory. |
5664c4
|
470 |
|
95f766
|
471 |
See :ref:`view_and_route_predicates` for more information. |
5664c4
|
472 |
|
0b23b3
|
473 |
.. versionadded:: 1.4 |
9c8ec5
|
474 |
""" |
95f766
|
475 |
self._add_predicate( |
CM |
476 |
'route', |
|
477 |
name, |
|
478 |
factory, |
|
479 |
weighs_more_than=weighs_more_than, |
|
480 |
weighs_less_than=weighs_less_than |
|
481 |
) |
9c8ec5
|
482 |
|
CM |
483 |
def add_default_route_predicates(self): |
c7974f
|
484 |
p = pyramid.predicates |
9c8ec5
|
485 |
for (name, factory) in ( |
8ec8e2
|
486 |
('xhr', p.XHRPredicate), |
CM |
487 |
('request_method', p.RequestMethodPredicate), |
|
488 |
('path_info', p.PathInfoPredicate), |
|
489 |
('request_param', p.RequestParamPredicate), |
|
490 |
('header', p.HeaderPredicate), |
|
491 |
('accept', p.AcceptPredicate), |
c7337b
|
492 |
('effective_principals', p.EffectivePrincipalsPredicate), |
8ec8e2
|
493 |
('custom', p.CustomPredicate), |
CM |
494 |
('traverse', p.TraversePredicate), |
9c8ec5
|
495 |
): |
CM |
496 |
self.add_route_predicate(name, factory) |
503bbb
|
497 |
|
5bf23f
|
498 |
def get_routes_mapper(self): |
CM |
499 |
""" Return the :term:`routes mapper` object associated with |
|
500 |
this configurator's :term:`registry`.""" |
|
501 |
mapper = self.registry.queryUtility(IRoutesMapper) |
|
502 |
if mapper is None: |
|
503 |
mapper = RoutesMapper() |
|
504 |
self.registry.registerUtility(mapper, IRoutesMapper) |
|
505 |
return mapper |
|
506 |
|
efd61e
|
507 |
@contextlib.contextmanager |
HS |
508 |
def route_prefix_context(self, route_prefix): |
|
509 |
""" Return this configurator with the |
|
510 |
:attr:`pyramid.config.Configurator.route_prefix` attribute mutated to |
|
511 |
include the new ``route_prefix``. |
|
512 |
|
|
513 |
When the context exits, the ``route_prefix`` is reset to the original. |
|
514 |
|
|
515 |
Example Usage: |
|
516 |
|
|
517 |
>>> config = Configurator() |
|
518 |
>>> with config.route_prefix_context('foo'): |
|
519 |
... config.add_route('bar', '/bar') |
|
520 |
|
|
521 |
Arguments |
|
522 |
|
|
523 |
route_prefix |
|
524 |
|
|
525 |
A string suitable to be used as a route prefix, or ``None``. |
|
526 |
|
f6aee3
|
527 |
.. versionadded:: 1.10 |
efd61e
|
528 |
""" |
HS |
529 |
|
|
530 |
original_route_prefix = self.route_prefix |
|
531 |
|
|
532 |
if route_prefix is None: |
|
533 |
route_prefix = '' |
|
534 |
|
|
535 |
old_route_prefix = self.route_prefix |
|
536 |
if old_route_prefix is None: |
|
537 |
old_route_prefix = '' |
|
538 |
|
|
539 |
route_prefix = '{}/{}'.format( |
|
540 |
old_route_prefix.rstrip('/'), |
|
541 |
route_prefix.lstrip('/'), |
|
542 |
) |
|
543 |
|
|
544 |
route_prefix = route_prefix.strip('/') |
|
545 |
|
|
546 |
if not route_prefix: |
|
547 |
route_prefix = None |
|
548 |
|
|
549 |
self.begin() |
|
550 |
try: |
|
551 |
self.route_prefix = route_prefix |
|
552 |
yield |
|
553 |
|
|
554 |
finally: |
|
555 |
self.route_prefix = original_route_prefix |
|
556 |
self.end() |