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