1""" Utility functions for dealing with URLs in pyramid """ 2 3import os 4import warnings 5 6from repoze.lru import lru_cache 7 8from pyramid.interfaces import ( 9 IResourceURL, 10 IRoutesMapper, 11 IStaticURLInfo, 12 ) 13 14from pyramid.compat import ( 15 bytes_, 16 string_types, 17 ) 18from pyramid.encode import ( 19 url_quote, 20 urlencode, 21) 22from pyramid.path import caller_package 23from pyramid.threadlocal import get_current_registry 24 25from pyramid.traversal import ( 26 ResourceURL, 27 quote_path_segment, 28 ) 29 30PATH_SAFE = '/:@&+$,' # from webob 31QUERY_SAFE = '/?:@!$&\'()*+,;=' # RFC 3986 32ANCHOR_SAFE = QUERY_SAFE 33 34def parse_url_overrides(kw): 35 """Parse special arguments passed when generating urls. 36 37 The supplied dictionary is mutated, popping arguments as necessary. 38 Returns a 6-tuple of the format ``(app_url, scheme, host, port, 39 qs, anchor)``. 40 """ 41 anchor = '' 42 qs = '' 43 app_url = None 44 host = None 45 scheme = None 46 port = None 47 48 if '_query' in kw: 49 query = kw.pop('_query') 50 if isinstance(query, string_types): 51 qs = '?' + url_quote(query, QUERY_SAFE) 52 elif query: 53 qs = '?' + urlencode(query, doseq=True) 54 55 if '_anchor' in kw: 56 anchor = kw.pop('_anchor') 57 anchor = url_quote(anchor, ANCHOR_SAFE) 58 anchor = '#' + anchor 59 60 if '_app_url' in kw: 61 app_url = kw.pop('_app_url') 62 63 if '_host' in kw: 64 host = kw.pop('_host') 65 66 if '_scheme' in kw: 67 scheme = kw.pop('_scheme') 68 69 if '_port' in kw: 70 port = kw.pop('_port') 71 72 return app_url, scheme, host, port, qs, anchor 73 74class URLMethodsMixin(object): 75 """ Request methods mixin for BaseRequest having to do with URL 76 generation """ 77 78 def _partial_application_url(self, scheme=None, host=None, port=None): 79 """ 80 Construct the URL defined by request.application_url, replacing any 81 of the default scheme, host, or port portions with user-supplied 82 variants. 83 84 If ``scheme`` is passed as ``https``, and the ``port`` is *not* 85 passed, the ``port`` value is assumed to ``443``. Likewise, if 86 ``scheme`` is passed as ``http`` and ``port`` is not passed, the 87 ``port`` value is assumed to be ``80``. 88 """ 89 e = self.environ 90 if scheme is None: 91 scheme = e['wsgi.url_scheme'] 92 else: 93 if scheme == 'https': 94 if port is None: 95 port = '443' 96 if scheme == 'http': 97 if port is None: 98 port = '80' 99 if host is None: 100 host = e.get('HTTP_HOST') 101 if host is None: 102 host = e['SERVER_NAME'] 103 if port is None: 104 if ':' in host: 105 host, port = host.split(':', 1) 106 else: 107 port = e['SERVER_PORT'] 108 else: 109 port = str(port) 110 if ':' in host: 111 host, _ = host.split(':', 1) 112 if scheme == 'https': 113 if port == '443': 114 port = None 115 elif scheme == 'http': 116 if port == '80': 117 port = None 118 url = scheme + '://' + host 119 if port: 120 url += ':%s' % port 121 122 url_encoding = getattr(self, 'url_encoding', 'utf-8') # webob 1.2b3+ 123 bscript_name = bytes_(self.script_name, url_encoding) 124 return url + url_quote(bscript_name, PATH_SAFE) 125 126 def route_url(self, route_name, *elements, **kw): 127 """Generates a fully qualified URL for a named :app:`Pyramid` 128 :term:`route configuration`. 129 130 Use the route's ``name`` as the first positional argument. 131 Additional positional arguments (``*elements``) are appended to the 132 URL as path segments after it is generated. 133 134 Use keyword arguments to supply values which match any dynamic 135 path elements in the route definition. Raises a :exc:`KeyError` 136 exception if the URL cannot be generated for any reason (not 137 enough arguments, for example). 138 139 For example, if you've defined a route named "foobar" with the path 140 ``{foo}/{bar}/*traverse``:: 141 142 request.route_url('foobar', 143 foo='1') => <KeyError exception> 144 request.route_url('foobar', 145 foo='1', 146 bar='2') => <KeyError exception> 147 request.route_url('foobar', 148 foo='1', 149 bar='2', 150 traverse=('a','b')) => http://e.com/1/2/a/b 151 request.route_url('foobar', 152 foo='1', 153 bar='2', 154 traverse='/a/b') => http://e.com/1/2/a/b 155 156 Values replacing ``:segment`` arguments can be passed as strings 157 or Unicode objects. They will be encoded to UTF-8 and URL-quoted 158 before being placed into the generated URL. 159 160 Values replacing ``*remainder`` arguments can be passed as strings 161 *or* tuples of Unicode/string values. If a tuple is passed as a 162 ``*remainder`` replacement value, its values are URL-quoted and 163 encoded to UTF-8. The resulting strings are joined with slashes 164 and rendered into the URL. If a string is passed as a 165 ``*remainder`` replacement value, it is tacked on to the URL 166 after being URL-quoted-except-for-embedded-slashes. 167 168 If no ``_query`` keyword argument is provided, the request query string 169 will be returned in the URL. If it is present, it will be used to 170 compose a query string that will be tacked on to the end of the URL, 171 replacing any request query string. The value of ``_query`` may be a 172 sequence of two-tuples *or* a data structure with an ``.items()`` 173 method that returns a sequence of two-tuples (presumably a dictionary). 174 This data structure will be turned into a query string per the 175 documentation of :func:`pyramid.url.urlencode` function. This will 176 produce a query string in the ``x-www-form-urlencoded`` format. A 177 non-``x-www-form-urlencoded`` query string may be used by passing a 178 *string* value as ``_query`` in which case it will be URL-quoted 179 (e.g. query="foo bar" will become "foo%20bar"). However, the result 180 will not need to be in ``k=v`` form as required by 181 ``x-www-form-urlencoded``. After the query data is turned into a query 182 string, a leading ``?`` is prepended, and the resulting string is 183 appended to the generated URL. 184 185 .. note:: 186 187 Python data structures that are passed as ``_query`` which are 188 sequences or dictionaries are turned into a string under the same 189 rules as when run through :func:`urllib.urlencode` with the ``doseq`` 190 argument equal to ``True``. This means that sequences can be passed 191 as values, and a k=v pair will be placed into the query string for 192 each value. 193 194 .. versionchanged:: 1.5 195 Allow the ``_query`` option to be a string to enable alternative 196 encodings. 197 198 If a keyword argument ``_anchor`` is present, its string 199 representation will be quoted per :rfc:`3986#section-3.5` and used as 200 a named anchor in the generated URL 201 (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is 202 ``http://example.com/route/url``, the resulting generated URL will 203 be ``http://example.com/route/url#foo``). 204 205 .. note:: 206 207 If ``_anchor`` is passed as a string, it should be UTF-8 encoded. If 208 ``_anchor`` is passed as a Unicode object, it will be converted to 209 UTF-8 before being appended to the URL. 210 211 .. versionchanged:: 1.5 212 The ``_anchor`` option will be escaped instead of using 213 its raw string representation. 214 215 If both ``_anchor`` and ``_query`` are specified, the anchor 216 element will always follow the query element, 217 e.g. ``http://example.com?foo=1#bar``. 218 219 If any of the keyword arguments ``_scheme``, ``_host``, or ``_port`` 220 is passed and is non-``None``, the provided value will replace the 221 named portion in the generated URL. For example, if you pass 222 ``_host='foo.com'``, and the URL that would have been generated 223 without the host replacement is ``http://example.com/a``, the result 224 will be ``http://foo.com/a``. 225 226 Note that if ``_scheme`` is passed as ``https``, and ``_port`` is not 227 passed, the ``_port`` value is assumed to have been passed as 228 ``443``. Likewise, if ``_scheme`` is passed as ``http`` and 229 ``_port`` is not passed, the ``_port`` value is assumed to have been 230 passed as ``80``. To avoid this behavior, always explicitly pass 231 ``_port`` whenever you pass ``_scheme``. 232 233 If a keyword ``_app_url`` is present, it will be used as the 234 protocol/hostname/port/leading path prefix of the generated URL. 235 For example, using an ``_app_url`` of 236 ``http://example.com:8080/foo`` would cause the URL 237 ``http://example.com:8080/foo/fleeb/flub`` to be returned from 238 this function if the expansion of the route pattern associated 239 with the ``route_name`` expanded to ``/fleeb/flub``. If 240 ``_app_url`` is not specified, the result of 241 ``request.application_url`` will be used as the prefix (the 242 default). 243 244 If both ``_app_url`` and any of ``_scheme``, ``_host``, or ``_port`` 245 are passed, ``_app_url`` takes precedence and any values passed for 246 ``_scheme``, ``_host``, and ``_port`` will be ignored. 247 248 This function raises a :exc:`KeyError` if the URL cannot be 249 generated due to missing replacement names. Extra replacement 250 names are ignored. 251 252 If the route object which matches the ``route_name`` argument has 253 a :term:`pregenerator`, the ``*elements`` and ``**kw`` 254 arguments passed to this function might be augmented or changed. 255 """ 256 try: 257 reg = self.registry 258 except AttributeError: 259 reg = get_current_registry() # b/c 260 mapper = reg.getUtility(IRoutesMapper) 261 route = mapper.get_route(route_name) 262 263 if route is None: 264 raise KeyError('No such route named %s' % route_name) 265 266 if route.pregenerator is not None: 267 elements, kw = route.pregenerator(self, elements, kw) 268 269 app_url, scheme, host, port, qs, anchor = parse_url_overrides(kw) 270 271 if app_url is None: 272 if (scheme is not None or host is not None or port is not None): 273 app_url = self._partial_application_url(scheme, host, port) 274 else: 275 app_url = self.application_url 276 277 path = route.generate(kw) # raises KeyError if generate fails 278 279 if elements: 280 suffix = _join_elements(elements) 281 if not path.endswith('/'): 282 suffix = '/' + suffix 283 else: 284 suffix = '' 285 286 return app_url + path + suffix + qs + anchor 287 288 def route_path(self, route_name, *elements, **kw): 289 """ 290 Generates a path (aka a 'relative URL', a URL minus the host, scheme, 291 and port) for a named :app:`Pyramid` :term:`route configuration`. 292 293 This function accepts the same argument as 294 :meth:`pyramid.request.Request.route_url` and performs the same duty. 295 It just omits the host, port, and scheme information in the return 296 value; only the script_name, path, query parameters, and anchor data 297 are present in the returned string. 298 299 For example, if you've defined a route named 'foobar' with the path 300 ``/{foo}/{bar}``, this call to ``route_path``:: 301 302 request.route_path('foobar', foo='1', bar='2') 303 304 Will return the string ``/1/2``. 305 306 .. note:: 307 308 Calling ``request.route_path('route')`` is the same as calling 309 ``request.route_url('route', _app_url=request.script_name)``. 310 :meth:`pyramid.request.Request.route_path` is, in fact, 311 implemented in terms of :meth:`pyramid.request.Request.route_url` 312 in just this way. As a result, any ``_app_url`` passed within the 313 ``**kw`` values to ``route_path`` will be ignored. 314 """ 315 kw['_app_url'] = self.script_name 316 return self.route_url(route_name, *elements, **kw) 317 318 def resource_url(self, resource, *elements, **kw): 319 """ 320 321 Generate a string representing the absolute URL of the 322 :term:`resource` object based on the ``wsgi.url_scheme``, 323 ``HTTP_HOST`` or ``SERVER_NAME`` in the request, plus any 324 ``SCRIPT_NAME``. The overall result of this method is always a 325 UTF-8 encoded string. 326 327 Examples:: 328 329 request.resource_url(resource) => 330 331 http://example.com/ 332 333 request.resource_url(resource, 'a.html') => 334 335 http://example.com/a.html 336 337 request.resource_url(resource, 'a.html', query={'q':'1'}) => 338 339 http://example.com/a.html?q=1 340 341 request.resource_url(resource, 'a.html', anchor='abc') => 342 343 http://example.com/a.html#abc 344 345 request.resource_url(resource, app_url='') => 346 347 / 348 349 Any positional arguments passed in as ``elements`` must be strings 350 Unicode objects, or integer objects. These will be joined by slashes 351 and appended to the generated resource URL. Each of the elements 352 passed in is URL-quoted before being appended; if any element is 353 Unicode, it will converted to a UTF-8 bytestring before being 354 URL-quoted. If any element is an integer, it will be converted to its 355 string representation before being URL-quoted. 356 357 .. warning:: if no ``elements`` arguments are specified, the resource 358 URL will end with a trailing slash. If any 359 ``elements`` are used, the generated URL will *not* 360 end in a trailing slash. 361 362 If a keyword argument ``query`` is present, it will be used to compose 363 a query string that will be tacked on to the end of the URL. The value 364 of ``query`` may be a sequence of two-tuples *or* a data structure with 365 an ``.items()`` method that returns a sequence of two-tuples 366 (presumably a dictionary). This data structure will be turned into a 367 query string per the documentation of :func:``pyramid.url.urlencode`` 368 function. This will produce a query string in the 369 ``x-www-form-urlencoded`` encoding. A non-``x-www-form-urlencoded`` 370 query string may be used by passing a *string* value as ``query`` in 371 which case it will be URL-quoted (e.g. query="foo bar" will become 372 "foo%20bar"). However, the result will not need to be in ``k=v`` form 373 as required by ``x-www-form-urlencoded``. After the query data is 374 turned into a query string, a leading ``?`` is prepended, and the 375 resulting string is appended to the generated URL. 376 377 .. note:: 378 379 Python data structures that are passed as ``query`` which are 380 sequences or dictionaries are turned into a string under the same 381 rules as when run through :func:`urllib.urlencode` with the ``doseq`` 382 argument equal to ``True``. This means that sequences can be passed 383 as values, and a k=v pair will be placed into the query string for 384 each value. 385 386 .. versionchanged:: 1.5 387 Allow the ``query`` option to be a string to enable alternative 388 encodings. 389 390 If a keyword argument ``anchor`` is present, its string 391 representation will be used as a named anchor in the generated URL 392 (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is 393 ``http://example.com/resource/url``, the resulting generated URL will 394 be ``http://example.com/resource/url#foo``). 395 396 .. note:: 397 398 If ``anchor`` is passed as a string, it should be UTF-8 encoded. If 399 ``anchor`` is passed as a Unicode object, it will be converted to 400 UTF-8 before being appended to the URL. 401 402 .. versionchanged:: 1.5 403 The ``anchor`` option will be escaped instead of using 404 its raw string representation. 405 406 If both ``anchor`` and ``query`` are specified, the anchor element 407 will always follow the query element, 408 e.g. ``http://example.com?foo=1#bar``. 409 410 If any of the keyword arguments ``scheme``, ``host``, or ``port`` is 411 passed and is non-``None``, the provided value will replace the named 412 portion in the generated URL. For example, if you pass 413 ``host='foo.com'``, and the URL that would have been generated 414 without the host replacement is ``http://example.com/a``, the result 415 will be ``http://foo.com/a``. 416 417 If ``scheme`` is passed as ``https``, and an explicit ``port`` is not 418 passed, the ``port`` value is assumed to have been passed as ``443``. 419 Likewise, if ``scheme`` is passed as ``http`` and ``port`` is not 420 passed, the ``port`` value is assumed to have been passed as 421 ``80``. To avoid this behavior, always explicitly pass ``port`` 422 whenever you pass ``scheme``. 423 424 If a keyword argument ``app_url`` is passed and is not ``None``, it 425 should be a string that will be used as the port/hostname/initial 426 path portion of the generated URL instead of the default request 427 application URL. For example, if ``app_url='http://foo'``, then the 428 resulting url of a resource that has a path of ``/baz/bar`` will be 429 ``http://foo/baz/bar``. If you want to generate completely relative 430 URLs with no leading scheme, host, port, or initial path, you can 431 pass ``app_url=''``. Passing ``app_url=''`` when the resource path is 432 ``/baz/bar`` will return ``/baz/bar``. 433 434 .. versionadded:: 1.3 435 ``app_url`` 436 437 If ``app_url`` is passed and any of ``scheme``, ``port``, or ``host`` 438 are also passed, ``app_url`` will take precedence and the values 439 passed for ``scheme``, ``host``, and/or ``port`` will be ignored. 440 441 If the ``resource`` passed in has a ``__resource_url__`` method, it 442 will be used to generate the URL (scheme, host, port, path) for the 443 base resource which is operated upon by this function. 444 445 .. seealso:: 446 447 See also :ref:`overriding_resource_url_generation`. 448 449 .. versionadded:: 1.5 450 ``route_name``, ``route_kw``, and ``route_remainder_name`` 451 452 If ``route_name`` is passed, this function will delegate its URL 453 production to the ``route_url`` function. Calling 454 ``resource_url(someresource, 'element1', 'element2', query={'a':1}, 455 route_name='blogentry')`` is roughly equivalent to doing:: 456 457 remainder_path = request.resource_path(someobject) 458 url = request.route_url( 459 'blogentry', 460 'element1', 461 'element2', 462 _query={'a':'1'}, 463 traverse=traversal_path, 464 ) 465 466 It is only sensible to pass ``route_name`` if the route being named has 467 a ``*remainder`` stararg value such as ``*traverse``. The remainder 468 value will be ignored in the output otherwise. 469 470 By default, the resource path value will be passed as the name 471 ``traverse`` when ``route_url`` is called. You can influence this by 472 passing a different ``route_remainder_name`` value if the route has a 473 different ``*stararg`` value at its end. For example if the route 474 pattern you want to replace has a ``*subpath`` stararg ala 475 ``/foo*subpath``:: 476 477 request.resource_url( 478 resource, 479 route_name='myroute', 480 route_remainder_name='subpath' 481 ) 482 483 If ``route_name`` is passed, it is also permissible to pass 484 ``route_kw``, which will passed as additional keyword arguments to 485 ``route_url``. Saying ``resource_url(someresource, 'element1', 486 'element2', route_name='blogentry', route_kw={'id':'4'}, 487 _query={'a':'1'})`` is roughly equivalent to:: 488 489 remainder_path = request.resource_path_tuple(someobject) 490 kw = {'id':'4', '_query':{'a':'1'}, 'traverse':traversal_path} 491 url = request.route_url( 492 'blogentry', 493 'element1', 494 'element2', 495 **kw, 496 ) 497 498 If ``route_kw`` or ``route_remainder_name`` is passed, but 499 ``route_name`` is not passed, both ``route_kw`` and 500 ``route_remainder_name`` will be ignored. If ``route_name`` 501 is passed, the ``__resource_url__`` method of the resource passed is 502 ignored unconditionally. This feature is incompatible with 503 resources which generate their own URLs. 504 505 .. note:: 506 507 If the :term:`resource` used is the result of a :term:`traversal`, it 508 must be :term:`location`-aware. The resource can also be the context 509 of a :term:`URL dispatch`; contexts found this way do not need to be 510 location-aware. 511 512 .. note:: 513 514 If a 'virtual root path' is present in the request environment (the 515 value of the WSGI environ key ``HTTP_X_VHM_ROOT``), and the resource 516 was obtained via :term:`traversal`, the URL path will not include the 517 virtual root prefix (it will be stripped off the left hand side of 518 the generated URL). 519 520 .. note:: 521 522 For backwards compatibility purposes, this method is also 523 aliased as the ``model_url`` method of request. 524 """ 525 try: 526 reg = self.registry 527 except AttributeError: 528 reg = get_current_registry() # b/c 529 530 url_adapter = reg.queryMultiAdapter((resource, self), IResourceURL) 531 if url_adapter is None: 532 url_adapter = ResourceURL(resource, self) 533 534 virtual_path = getattr(url_adapter, 'virtual_path', None) 535 536 if virtual_path is None: 537 # old-style IContextURL adapter (Pyramid 1.2 and previous) 538 warnings.warn( 539 'Pyramid is using an IContextURL adapter to generate a ' 540 'resource URL; any "app_url", "host", "port", or "scheme" ' 541 'arguments passed to resource_url are being ignored. To ' 542 'avoid this behavior, as of Pyramid 1.3, register an ' 543 'IResourceURL adapter instead of an IContextURL ' 544 'adapter for the resource type(s). IContextURL adapters ' 545 'will be ignored in a later major release of Pyramid.', 546 DeprecationWarning, 547 2) 548 549 resource_url = url_adapter() 550 551 else: 552 # IResourceURL adapter (Pyramid 1.3 and after) 553 app_url = None 554 scheme = None 555 host = None 556 port = None 557 558 if 'route_name' in kw: 559 newkw = {} 560 route_name = kw['route_name'] 561 remainder = getattr(url_adapter, 'virtual_path_tuple', None) 562 if remainder is None: 563 # older user-supplied IResourceURL adapter without 1.5 564 # virtual_path_tuple 565 remainder = tuple(url_adapter.virtual_path.split('/')) 566 remainder_name = kw.get('route_remainder_name', 'traverse') 567 newkw[remainder_name] = remainder 568 569 for name in ( 570 'app_url', 'scheme', 'host', 'port', 'query', 'anchor' 571 ): 572 val = kw.get(name, None) 573 if val is not None: 574 newkw['_' + name] = val 575 576 if 'route_kw' in kw: 577 route_kw = kw.get('route_kw') 578 if route_kw is not None: 579 newkw.update(route_kw) 580 581 return self.route_url(route_name, *elements, **newkw) 582 583 if 'app_url' in kw: 584 app_url = kw['app_url'] 585 586 if 'scheme' in kw: 587 scheme = kw['scheme'] 588 589 if 'host' in kw: 590 host = kw['host'] 591 592 if 'port' in kw: 593 port = kw['port'] 594 595 if app_url is None: 596 if scheme or host or port: 597 app_url = self._partial_application_url(scheme, host, port) 598 else: 599 app_url = self.application_url 600 601 resource_url = None 602 local_url = getattr(resource, '__resource_url__', None) 603 604 if local_url is not None: 605 # the resource handles its own url generation 606 d = dict( 607 virtual_path=virtual_path, 608 physical_path=url_adapter.physical_path, 609 app_url=app_url, 610 ) 611 612 # allow __resource_url__ to punt by returning None 613 resource_url = local_url(self, d) 614 615 if resource_url is None: 616 # the resource did not handle its own url generation or the 617 # __resource_url__ function returned None 618 resource_url = app_url + virtual_path 619 620 qs = '' 621 anchor = '' 622 623 if 'query' in kw: 624 query = kw['query'] 625 if isinstance(query, string_types): 626 qs = '?' + url_quote(query, QUERY_SAFE) 627 elif query: 628 qs = '?' + urlencode(query, doseq=True) 629 630 if 'anchor' in kw: 631 anchor = kw['anchor'] 632 anchor = url_quote(anchor, ANCHOR_SAFE) 633 anchor = '#' + anchor 634 635 if elements: 636 suffix = _join_elements(elements) 637 else: 638 suffix = '' 639 640 return resource_url + suffix + qs + anchor 641 642 model_url = resource_url # b/w compat forever 643 644 def resource_path(self, resource, *elements, **kw): 645 """ 646 Generates a path (aka a 'relative URL', a URL minus the host, scheme, 647 and port) for a :term:`resource`. 648 649 This function accepts the same argument as 650 :meth:`pyramid.request.Request.resource_url` and performs the same 651 duty. It just omits the host, port, and scheme information in the 652 return value; only the script_name, path, query parameters, and 653 anchor data are present in the returned string. 654 655 .. note:: 656 657 Calling ``request.resource_path(resource)`` is the same as calling 658 ``request.resource_path(resource, app_url=request.script_name)``. 659 :meth:`pyramid.request.Request.resource_path` is, in fact, 660 implemented in terms of 661 :meth:`pyramid.request.Request.resource_url` in just this way. As 662 a result, any ``app_url`` passed within the ``**kw`` values to 663 ``route_path`` will be ignored. ``scheme``, ``host``, and 664 ``port`` are also ignored. 665 """ 666 kw['app_url'] = self.script_name 667 return self.resource_url(resource, *elements, **kw) 668 669 def static_url(self, path, **kw): 670 """ 671 Generates a fully qualified URL for a static :term:`asset`. 672 The asset must live within a location defined via the 673 :meth:`pyramid.config.Configurator.add_static_view` 674 :term:`configuration declaration` (see :ref:`static_assets_section`). 675 676 Example:: 677 678 request.static_url('mypackage:static/foo.css') => 679 680 http://example.com/static/foo.css 681 682 683 The ``path`` argument points at a file or directory on disk which 684 a URL should be generated for. The ``path`` may be either a 685 relative path (e.g. ``static/foo.css``) or an absolute path (e.g. 686 ``/abspath/to/static/foo.css``) or a :term:`asset specification` 687 (e.g. ``mypackage:static/foo.css``). 688 689 The purpose of the ``**kw`` argument is the same as the purpose of 690 the :meth:`pyramid.request.Request.route_url` ``**kw`` argument. See 691 the documentation for that function to understand the arguments which 692 you can provide to it. However, typically, you don't need to pass 693 anything as ``*kw`` when generating a static asset URL. 694 695 This function raises a :exc:`ValueError` if a static view 696 definition cannot be found which matches the path specification. 697 698 """ 699 if not os.path.isabs(path): 700 if ':' not in path: 701 # if it's not a package:relative/name and it's not an 702 # /absolute/path it's a relative/path; this means its relative 703 # to the package in which the caller's module is defined. 704 package = caller_package() 705 path = '%s:%s' % (package.__name__, path) 706 707 try: 708 reg = self.registry 709 except AttributeError: 710 reg = get_current_registry() # b/c 711 712 info = reg.queryUtility(IStaticURLInfo) 713 if info is None: 714 raise ValueError('No static URL definition matching %s' % path) 715 716 return info.generate(path, self, **kw) 717 718 def static_path(self, path, **kw): 719 """ 720 Generates a path (aka a 'relative URL', a URL minus the host, scheme, 721 and port) for a static resource. 722 723 This function accepts the same argument as 724 :meth:`pyramid.request.Request.static_url` and performs the 725 same duty. It just omits the host, port, and scheme information in 726 the return value; only the script_name, path, query parameters, and 727 anchor data are present in the returned string. 728 729 Example:: 730 731 request.static_path('mypackage:static/foo.css') => 732 733 /static/foo.css 734 735 .. note:: 736 737 Calling ``request.static_path(apath)`` is the same as calling 738 ``request.static_url(apath, _app_url=request.script_name)``. 739 :meth:`pyramid.request.Request.static_path` is, in fact, implemented 740 in terms of `:meth:`pyramid.request.Request.static_url` in just this 741 way. As a result, any ``_app_url`` passed within the ``**kw`` values 742 to ``static_path`` will be ignored. 743 """ 744 if not os.path.isabs(path): 745 if ':' not in path: 746 # if it's not a package:relative/name and it's not an 747 # /absolute/path it's a relative/path; this means its relative 748 # to the package in which the caller's module is defined. 749 package = caller_package() 750 path = '%s:%s' % (package.__name__, path) 751 752 kw['_app_url'] = self.script_name 753 return self.static_url(path, **kw) 754 755 def current_route_url(self, *elements, **kw): 756 """ 757 Generates a fully qualified URL for a named :app:`Pyramid` 758 :term:`route configuration` based on the 'current route'. 759 760 This function supplements 761 :meth:`pyramid.request.Request.route_url`. It presents an easy way to 762 generate a URL for the 'current route' (defined as the route which 763 matched when the request was generated). 764 765 The arguments to this method have the same meaning as those with the 766 same names passed to :meth:`pyramid.request.Request.route_url`. It 767 also understands an extra argument which ``route_url`` does not named 768 ``_route_name``. 769 770 The route name used to generate a URL is taken from either the 771 ``_route_name`` keyword argument or the name of the route which is 772 currently associated with the request if ``_route_name`` was not 773 passed. Keys and values from the current request :term:`matchdict` 774 are combined with the ``kw`` arguments to form a set of defaults 775 named ``newkw``. Then ``request.route_url(route_name, *elements, 776 **newkw)`` is called, returning a URL. 777 778 Examples follow. 779 780 If the 'current route' has the route pattern ``/foo/{page}`` and the 781 current url path is ``/foo/1`` , the matchdict will be 782 ``{'page':'1'}``. The result of ``request.current_route_url()`` in 783 this situation will be ``/foo/1``. 784 785 If the 'current route' has the route pattern ``/foo/{page}`` and the 786 current url path is ``/foo/1``, the matchdict will be 787 ``{'page':'1'}``. The result of 788 ``request.current_route_url(page='2')`` in this situation will be 789 ``/foo/2``. 790 791 Usage of the ``_route_name`` keyword argument: if our routing table 792 defines routes ``/foo/{action}`` named 'foo' and 793 ``/foo/{action}/{page}`` named ``fooaction``, and the current url 794 pattern is ``/foo/view`` (which has matched the ``/foo/{action}`` 795 route), we may want to use the matchdict args to generate a URL to 796 the ``fooaction`` route. In this scenario, 797 ``request.current_route_url(_route_name='fooaction', page='5')`` 798 Will return string like: ``/foo/view/5``. 799 800 """ 801 if '_route_name' in kw: 802 route_name = kw.pop('_route_name') 803 else: 804 route = getattr(self, 'matched_route', None) 805 route_name = getattr(route, 'name', None) 806 if route_name is None: 807 raise ValueError('Current request matches no route') 808 809 if '_query' not in kw: 810 kw['_query'] = self.GET 811 812 newkw = {} 813 newkw.update(self.matchdict) 814 newkw.update(kw) 815 return self.route_url(route_name, *elements, **newkw) 816 817 def current_route_path(self, *elements, **kw): 818 """ 819 Generates a path (aka a 'relative URL', a URL minus the host, scheme, 820 and port) for the :app:`Pyramid` :term:`route configuration` matched 821 by the current request. 822 823 This function accepts the same argument as 824 :meth:`pyramid.request.Request.current_route_url` and performs the 825 same duty. It just omits the host, port, and scheme information in 826 the return value; only the script_name, path, query parameters, and 827 anchor data are present in the returned string. 828 829 For example, if the route matched by the current request has the 830 pattern ``/{foo}/{bar}``, this call to ``current_route_path``:: 831 832 request.current_route_path(foo='1', bar='2') 833 834 Will return the string ``/1/2``. 835 836 .. note:: 837 838 Calling ``request.current_route_path('route')`` is the same 839 as calling ``request.current_route_url('route', 840 _app_url=request.script_name)``. 841 :meth:`pyramid.request.Request.current_route_path` is, in fact, 842 implemented in terms of 843 :meth:`pyramid.request.Request.current_route_url` in just this 844 way. As a result, any ``_app_url`` passed within the ``**kw`` 845 values to ``current_route_path`` will be ignored. 846 """ 847 kw['_app_url'] = self.script_name 848 return self.current_route_url(*elements, **kw) 849 850 851def route_url(route_name, request, *elements, **kw): 852 """ 853 This is a backwards compatibility function. Its result is the same as 854 calling:: 855 856 request.route_url(route_name, *elements, **kw) 857 858 See :meth:`pyramid.request.Request.route_url` for more information. 859 """ 860 return request.route_url(route_name, *elements, **kw) 861 862def route_path(route_name, request, *elements, **kw): 863 """ 864 This is a backwards compatibility function. Its result is the same as 865 calling:: 866 867 request.route_path(route_name, *elements, **kw) 868 869 See :meth:`pyramid.request.Request.route_path` for more information. 870 """ 871 return request.route_path(route_name, *elements, **kw) 872 873def resource_url(resource, request, *elements, **kw): 874 """ 875 This is a backwards compatibility function. Its result is the same as 876 calling:: 877 878 request.resource_url(resource, *elements, **kw) 879 880 See :meth:`pyramid.request.Request.resource_url` for more information. 881 """ 882 return request.resource_url(resource, *elements, **kw) 883 884model_url = resource_url # b/w compat (forever) 885 886 887def static_url(path, request, **kw): 888 """ 889 This is a backwards compatibility function. Its result is the same as 890 calling:: 891 892 request.static_url(path, **kw) 893 894 See :meth:`pyramid.request.Request.static_url` for more information. 895 """ 896 if not os.path.isabs(path): 897 if ':' not in path: 898 # if it's not a package:relative/name and it's not an 899 # /absolute/path it's a relative/path; this means its relative 900 # to the package in which the caller's module is defined. 901 package = caller_package() 902 path = '%s:%s' % (package.__name__, path) 903 return request.static_url(path, **kw) 904 905 906def static_path(path, request, **kw): 907 """ 908 This is a backwards compatibility function. Its result is the same as 909 calling:: 910 911 request.static_path(path, **kw) 912 913 See :meth:`pyramid.request.Request.static_path` for more information. 914 """ 915 if not os.path.isabs(path): 916 if ':' not in path: 917 # if it's not a package:relative/name and it's not an 918 # /absolute/path it's a relative/path; this means its relative 919 # to the package in which the caller's module is defined. 920 package = caller_package() 921 path = '%s:%s' % (package.__name__, path) 922 return request.static_path(path, **kw) 923 924def current_route_url(request, *elements, **kw): 925 """ 926 This is a backwards compatibility function. Its result is the same as 927 calling:: 928 929 request.current_route_url(*elements, **kw) 930 931 See :meth:`pyramid.request.Request.current_route_url` for more 932 information. 933 """ 934 return request.current_route_url(*elements, **kw) 935 936def current_route_path(request, *elements, **kw): 937 """ 938 This is a backwards compatibility function. Its result is the same as 939 calling:: 940 941 request.current_route_path(*elements, **kw) 942 943 See :meth:`pyramid.request.Request.current_route_path` for more 944 information. 945 """ 946 return request.current_route_path(*elements, **kw) 947 948@lru_cache(1000) 949def _join_elements(elements): 950 return '/'.join([quote_path_segment(s, safe=':@&+$,') for s in elements]) 951