From fb90f0166728af40142ed9a31039d26ca3f97c73 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sun, 14 Aug 2011 04:58:34 -0400 Subject: - The ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url`` functions in the ``pyramid.url`` package now delegate to a method on the request they've been passed, instead of the other way around. The pyramid.request.Request object now inherits from a mixin named pyramid.url.URLMethodsMixin to make this possible, and all url/path generation logic is embedded in this mixin. - Narrative and API documentation which used the ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url`` functions in the ``pyramid.url`` package have now been changed to use eponymous methods of the request instead. --- CHANGES.txt | 12 + docs/narr/assets.rst | 60 +-- docs/narr/commandline.rst | 2 +- docs/narr/hooks.rst | 19 +- docs/narr/resources.rst | 92 +++-- docs/narr/traversal.rst | 4 +- docs/narr/urldispatch.rst | 15 +- docs/narr/vhosting.rst | 4 +- pyramid/config.py | 57 +-- pyramid/interfaces.py | 13 +- pyramid/request.py | 139 +------ pyramid/testing.py | 3 +- pyramid/tests/test_testing.py | 9 +- pyramid/tests/test_url.py | 426 ++++++++++++---------- pyramid/url.py | 830 +++++++++++++++++++++++------------------- 15 files changed, 838 insertions(+), 847 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3432f3954..f420b5299 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -71,6 +71,13 @@ Internal (it inherits from ``dict``), and it's the value that is passed to templates as a top-level dictionary. +- The ``route_url``, ``route_path``, ``resource_url``, ``static_url``, and + ``current_route_url`` functions in the ``pyramid.url`` package now delegate + to a method on the request they've been passed, instead of the other way + around. The pyramid.request.Request object now inherits from a mixin named + pyramid.url.URLMethodsMixin to make this possible, and all url/path + generation logic is embedded in this mixin. + Deprecations ------------ @@ -101,6 +108,11 @@ Backwards Incompatibilities Documentation ------------- +- Narrative and API documentation which used the ``route_url``, + ``route_path``, ``resource_url``, ``static_url``, and ``current_route_url`` + functions in the ``pyramid.url`` package have now been changed to use + eponymous methods of the request instead. + - Added a section entitled "Using a Route Prefix to Compose Applications" to the "URL Dispatch" narrative documentation chapter. diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst index e609a3eab..c8508f1b5 100644 --- a/docs/narr/assets.rst +++ b/docs/narr/assets.rst @@ -165,7 +165,8 @@ Instead of representing a URL prefix, the ``name`` argument of a call to *URL*. Each of examples we've seen so far have shown usage of the ``name`` argument as a URL prefix. However, when ``name`` is a *URL*, static assets can be served from an external webserver. In this mode, the ``name`` is used -as the URL prefix when generating a URL using :func:`pyramid.url.static_url`. +as the URL prefix when generating a URL using +:meth:`pyramid.request.Request.static_url`. For example, :meth:`~pyramid.config.Configurator.add_static_view` may be fed a ``name`` argument which is ``http://example.com/images``: @@ -179,13 +180,13 @@ be fed a ``name`` argument which is ``http://example.com/images``: Because :meth:`~pyramid.config.Configurator.add_static_view` is provided with a ``name`` argument that is the URL ``http://example.com/images``, subsequent -calls to :func:`~pyramid.url.static_url` with paths that start with the -``path`` argument passed to +calls to :meth:`~pyramid.request.Request.static_url` with paths that start +with the ``path`` argument passed to :meth:`~pyramid.config.Configurator.add_static_view` will generate a URL something like ``http://example.com/images/logo.png``. The external webserver listening on ``example.com`` must be itself configured to respond -properly to such a request. The :func:`~pyramid.url.static_url` API is -discussed in more detail later in this chapter. +properly to such a request. The :meth:`~pyramid.request.Request.static_url` +API is discussed in more detail later in this chapter. .. index:: single: generating static asset urls @@ -199,9 +200,9 @@ Generating Static Asset URLs When a :meth:`~pyramid.config.Configurator.add_static_view` method is used to register a static asset directory, a special helper API named -:func:`pyramid.url.static_url` can be used to generate the appropriate URL -for an asset that lives in one of the directories named by the static -registration ``path`` attribute. +:meth:`pyramid.request.Request.static_url` can be used to generate the +appropriate URL for an asset that lives in one of the directories named by +the static registration ``path`` attribute. For example, let's assume you create a set of static declarations like so: @@ -219,18 +220,17 @@ visits a URL which begins with ``/static1``, and the assets in the visits a URL which begins with ``/static2``. You needn't generate the URLs to static assets "by hand" in such a -configuration. Instead, use the :func:`~pyramid.url.static_url` API to -generate them for you. For example: +configuration. Instead, use the :meth:`~pyramid.request.Request.static_url` +API to generate them for you. For example: .. code-block:: python :linenos: - from pyramid.url import static_url from pyramid.chameleon_zpt import render_template_to_response def my_view(request): - css_url = static_url('mypackage:assets/1/foo.css', request) - js_url = static_url('mypackage:assets/2/foo.js', request) + css_url = request.static_url('mypackage:assets/1/foo.css') + js_url = request.static_url('mypackage:assets/2/foo.js') return render_template_to_response('templates/my_template.pt', css_url = css_url, js_url = js_url) @@ -240,17 +240,18 @@ If the request "application URL" of the running system is ``http://example.com/static1/foo.css``. The ``js_url`` generated above would be ``http://example.com/static2/foo.js``. -One benefit of using the :func:`~pyramid.url.static_url` function rather than -constructing static URLs "by hand" is that if you need to change the ``name`` -of a static URL declaration, the generated URLs will continue to resolve -properly after the rename. +One benefit of using the :meth:`~pyramid.request.Request.static_url` function +rather than constructing static URLs "by hand" is that if you need to change +the ``name`` of a static URL declaration, the generated URLs will continue to +resolve properly after the rename. -URLs may also be generated by :func:`~pyramid.url.static_url` to static assets -that live *outside* the :app:`Pyramid` application. This will happen when -the :meth:`~pyramid.config.Configurator.add_static_view` API associated with -the path fed to :func:`~pyramid.url.static_url` is a *URL* instead of a view -name. For example, the ``name`` argument may be ``http://example.com`` while -the the ``path`` given may be ``mypackage:images``: +URLs may also be generated by :meth:`~pyramid.request.Request.static_url` to +static assets that live *outside* the :app:`Pyramid` application. This will +happen when the :meth:`~pyramid.config.Configurator.add_static_view` API +associated with the path fed to :meth:`~pyramid.request.Request.static_url` +is a *URL* instead of a view name. For example, the ``name`` argument may be +``http://example.com`` while the the ``path`` given may be +``mypackage:images``: .. code-block:: python :linenos: @@ -265,14 +266,14 @@ assets which begin with ``mypackage:images`` will be prefixed with .. code-block:: python :linenos: - static_url('mypackage:images/logo.png', request) + request.static_url('mypackage:images/logo.png') # -> http://example.com/images/logo.png -Using :func:`~pyramid.url.static_url` in conjunction with a +Using :meth:`~pyramid.request.Request.static_url` in conjunction with a :meth:`~pyramid.configuration.Configurator.add_static_view` makes it possible to put static media on a separate webserver during production (if the -``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is a -URL), while keeping static media package-internal and served by the +``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is +a URL), while keeping static media package-internal and served by the development webserver during development (if the ``name`` argument to :meth:`~pyramid.config.Configurator.add_static_view` is a URL prefix). To create such a circumstance, we suggest using the @@ -298,8 +299,9 @@ dispatch`, you may want static assets to only be available as a fallback if no previous route matches. Alternately, you might like to serve a particular static asset manually, because its download requires authentication. -Note that you cannot use the :func:`~pyramid.url.static_url` API to generate -URLs against assets made accessible by registering a custom static view. +Note that you cannot use the :meth:`~pyramid.request.Request.static_url` API +to generate URLs against assets made accessible by registering a custom +static view. Root-Relative Custom Static View (URL Dispatch Only) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst index 97004d2b8..1c9d0b15c 100644 --- a/docs/narr/commandline.rst +++ b/docs/narr/commandline.rst @@ -532,7 +532,7 @@ Now you can readily use Pyramid's APIs for generating URLs: .. code-block:: python - route_url('verify', env['request'], code='1337') + env['request'].route_url('verify', code='1337') # will return 'https://example.com/prefix/verify/1337' Cleanup diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 50758f327..df5339c8a 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -468,17 +468,18 @@ when the application :term:`root factory` returned an instance of the .. _changing_resource_url: -Changing How :mod:`pyramid.url.resource_url` Generates a URL ------------------------------------------------------------- +Changing How :meth:`pyramid.request.Request.resource_url` Generates a URL +------------------------------------------------------------------------- When you add a traverser as described in :ref:`changing_the_traverser`, it's -often convenient to continue to use the :func:`pyramid.url.resource_url` API. -However, since the way traversal is done will have been modified, the URLs it -generates by default may be incorrect. +often convenient to continue to use the +:meth:`pyramid.request.Request.resource_url` API. However, since the way +traversal is done will have been modified, the URLs it generates by default +may be incorrect. If you've added a traverser, you can change how -:func:`~pyramid.url.resource_url` generates a URL for a specific type of -resource by adding a registerAdapter call for +:meth:`~pyramid.request.Request.resource_url` generates a URL for a specific +type of resource by adding a registerAdapter call for :class:`pyramid.interfaces.IContextURL` to your application: .. code-block:: python @@ -493,8 +494,8 @@ resource by adding a registerAdapter call for IContextURL) In the above example, the ``myapp.traversal.URLGenerator`` class will be used -to provide services to :func:`~pyramid.url.resource_url` any time the -:term:`context` passed to ``resource_url`` is of class +to provide services to :meth:`~pyramid.request.Request.resource_url` any time +the :term:`context` passed to ``resource_url`` is of class ``myapp.resources.MyRoot``. The second argument in the ``(MyRoot, Interface)`` tuple represents the type of interface that must be possessed by the :term:`request` (in this case, any interface, represented by diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst index 0e0d00020..9335906a0 100644 --- a/docs/narr/resources.rst +++ b/docs/narr/resources.rst @@ -45,8 +45,8 @@ Also: - A resource is exposed to :term:`view` code as the :term:`context` of a view. -- Various helpful :app:`Pyramid` API methods expect a resource as an - argument (e.g. :func:`~pyramid.url.resource_url` and others). +- Various helpful :app:`Pyramid` API methods expect a resource as an argument + (e.g. :meth:`~pyramid.request.Request.resource_url` and others). .. index:: single: resource tree @@ -160,14 +160,14 @@ resource in the resource tree, you will eventually come to the root resource, just like if you keep executing the ``cd ..`` filesystem command, eventually you will reach the filesystem root directory. -.. warning:: If your root resource has a ``__name__`` argument - that is not ``None`` or the empty string, URLs returned by the - :func:`~pyramid.url.resource_url` function and paths generated by - the :func:`~pyramid.traversal.resource_path` and - :func:`~pyramid.traversal.resource_path_tuple` APIs will be - generated improperly. The value of ``__name__`` will be prepended - to every path and URL generated (as opposed to a single leading - slash or empty tuple element). +.. warning:: If your root resource has a ``__name__`` argument that is not + ``None`` or the empty string, URLs returned by the + :func:`~pyramid.request.Request.resource_url` function and paths generated + by the :func:`~pyramid.traversal.resource_path` and + :func:`~pyramid.traversal.resource_path_tuple` APIs will be generated + improperly. The value of ``__name__`` will be prepended to every path and + URL generated (as opposed to a single leading slash or empty tuple + element). .. sidebar:: Using :mod:`pyramid_traversalwrapper` @@ -192,7 +192,8 @@ you will reach the filesystem root directory. Applications which use tree-walking :app:`Pyramid` APIs require location-aware resources. These APIs include (but are not limited to) -:func:`~pyramid.url.resource_url`, :func:`~pyramid.traversal.find_resource`, +:meth:`~pyramid.request.Request.resource_url`, +:func:`~pyramid.traversal.find_resource`, :func:`~pyramid.traversal.find_root`, :func:`~pyramid.traversal.find_interface`, :func:`~pyramid.traversal.resource_path`, @@ -215,23 +216,23 @@ Generating The URL Of A Resource -------------------------------- If your resources are :term:`location` aware, you can use the -:func:`pyramid.url.resource_url` API to generate a URL for the resource. -This URL will use the resource's position in the parent tree to create a -resource path, and it will prefix the path with the current application URL -to form a fully-qualified URL with the scheme, host, port, and path. You can -also pass extra arguments to :func:`~pyramid.url.resource_url` to influence -the generated URL. +:meth:`pyramid.request.Request.resource_url` API to generate a URL for the +resource. This URL will use the resource's position in the parent tree to +create a resource path, and it will prefix the path with the current +application URL to form a fully-qualified URL with the scheme, host, port, +and path. You can also pass extra arguments to +:meth:`~pyramid.request.Request.resource_url` to influence the generated URL. -The simplest call to :func:`~pyramid.url.resource_url` looks like this: +The simplest call to :meth:`~pyramid.request.Request.resource_url` looks like +this: .. code-block:: python :linenos: - from pyramid.url import resource_url - url = resource_url(resource, request) + url = request.resource_url(resource, request) -The ``request`` passed to ``resource_url`` in the above example is an -instance of a :app:`Pyramid` :term:`request` object. +The ``request`` in the above example is an instance of a :app:`Pyramid` +:term:`request` object. If the resource referred to as ``resource`` in the above example was the root resource, and the host that was used to contact the server was @@ -240,51 +241,46 @@ However, if the resource was a child of the root resource named ``a``, the generated URL would be ``http://example.com/a/``. A slash is appended to all resource URLs when -:func:`~pyramid.url.resource_url` is used to generate them in this simple -manner, because resources are "places" in the hierarchy, and URLs are meant -to be clicked on to be visited. Relative URLs that you include on HTML pages -rendered as the result of the default view of a resource are more +:meth:`~pyramid.request.Request.resource_url` is used to generate them in +this simple manner, because resources are "places" in the hierarchy, and URLs +are meant to be clicked on to be visited. Relative URLs that you include on +HTML pages rendered as the result of the default view of a resource are more apt to be relative to these resources than relative to their parent. -You can also pass extra elements to :func:`~pyramid.url.resource_url`: +You can also pass extra elements to +:meth:`~pyramid.request.Request.resource_url`: .. code-block:: python :linenos: - from pyramid.url import resource_url - url = resource_url(resource, request, 'foo', 'bar') + url = request.resource_url(resource, 'foo', 'bar') If the resource referred to as ``resource`` in the above example was the root resource, and the host that was used to contact the server was ``example.com``, the URL generated would be ``http://example.com/foo/bar``. Any number of extra elements can be passed to -:func:`~pyramid.url.resource_url` as extra positional arguments. When extra -elements are passed, they are appended to the resource's URL. A slash is not -appended to the final segment when elements are passed. +:meth:`~pyramid.request.Request.resource_url` as extra positional arguments. +When extra elements are passed, they are appended to the resource's URL. A +slash is not appended to the final segment when elements are passed. You can also pass a query string: .. code-block:: python :linenos: - from pyramid.url import resource_url - url = resource_url(resource, request, query={'a':'1'}) + url = request.resource_url(resource, query={'a':'1'}) If the resource referred to as ``resource`` in the above example was the root resource, and the host that was used to contact the server was ``example.com``, the URL generated would be ``http://example.com/?a=1``. When a :term:`virtual root` is active, the URL generated by -:func:`~pyramid.url.resource_url` for a resource may be "shorter" than its -physical tree path. See :ref:`virtual_root_support` for more information -about virtually rooting a resource. - -The shortcut method of the :term:`request` named -:meth:`pyramid.request.Request.resource_url` can be used instead of -:func:`~pyramid.url.resource_url` to generate a resource URL. +:meth:`~pyramid.request.Request.resource_url` for a resource may be "shorter" +than its physical tree path. See :ref:`virtual_root_support` for more +information about virtually rooting a resource. For more information about generating resource URLs, see the documentation -for :func:`pyramid.url.resource_url`. +for :meth:`pyramid.request.Request.resource_url`. .. index:: pair: resource URL generation; overriding @@ -295,14 +291,14 @@ Overriding Resource URL Generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If a resource object implements a ``__resource_url__`` method, this method -will be called when :func:`~pyramid.url.resource_url` is called to generate a -URL for the resource, overriding the default URL returned for the resource by -:func:`~pyramid.url.resource_url`. +will be called when :meth:`~pyramid.request.Request.resource_url` is called +to generate a URL for the resource, overriding the default URL returned for +the resource by :meth:`~pyramid.request.Request.resource_url`. The ``__resource_url__`` hook is passed two arguments: ``request`` and ``info``. ``request`` is the :term:`request` object passed to -:func:`~pyramid.url.resource_url`. ``info`` is a dictionary with two -keys: +:meth:`~pyramid.request.Request.resource_url`. ``info`` is a dictionary with +two keys: ``physical_path`` The "physical path" computed for the resource, as defined by @@ -334,7 +330,7 @@ or port number of the generated URL. Note that the URL generated by ``__resource_url__`` should be fully qualified, should end in a slash, and should not contain any query string or anchor elements (only path elements) to work best with -:func:`~pyramid.url.resource_url`. +:meth:`~pyramid.request.Request.resource_url`. .. index:: single: resource path generation diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst index aa36b4455..ef875c8f0 100644 --- a/docs/narr/traversal.rst +++ b/docs/narr/traversal.rst @@ -565,6 +565,6 @@ See the :ref:`view_config_chapter` chapter for detailed information about The :mod:`pyramid.traversal` module contains API functions that deal with traversal, such as traversal invocation from within application code. -The :func:`pyramid.url.resource_url` function generates a URL when given a -resource retrieved from a resource tree. +The :meth:`pyramid.request.Request.resource_url` method generates a URL when +given a resource retrieved from a resource tree. diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst index c0197743b..00d36cc76 100644 --- a/docs/narr/urldispatch.rst +++ b/docs/narr/urldispatch.rst @@ -765,20 +765,19 @@ Or provide the literal string ``/`` as the pattern: Generating Route URLs --------------------- -Use the :func:`pyramid.url.route_url` function to generate URLs based on -route patterns. For example, if you've configured a route with the ``name`` -"foo" and the ``pattern`` "{a}/{b}/{c}", you might do this. +Use the :meth:`pyramid.request.Request.route_url` method to generate URLs +based on route patterns. For example, if you've configured a route with the +``name`` "foo" and the ``pattern`` "{a}/{b}/{c}", you might do this. .. ignore-next-block .. code-block:: python :linenos: - from pyramid.url import route_url - url = route_url('foo', request, a='1', b='2', c='3') + url = request.route_url('foo', a='1', b='2', c='3') This would return something like the string ``http://example.com/1/2/3`` (at least if the current protocol and hostname implied ``http://example.com``). -See the :func:`~pyramid.url.route_url` API documentation for more +See the :meth:`~pyramid.request.Request.route_url` API documentation for more information. .. index:: @@ -1122,8 +1121,8 @@ In the above configuration, the ``show_users`` route will have an effective route pattern of ``/users/show``, instead of ``/show`` because the ``route_prefix`` argument will be prepended to the pattern. The route will then only match if the URL path is ``/users/show``, and when the -:func:`pyramid.url.route_url` function is called with the route name -``show_users``, it will generate a URL with that same path. +:meth:`pyramid.request.Request.route_url` function is called with the route +name ``show_users``, it will generate a URL with that same path. Route prefixes are recursive, so if a callable executed via an include itself turns around and includes another callable, the second-level route prefix diff --git a/docs/narr/vhosting.rst b/docs/narr/vhosting.rst index 5679cc2e2..ddbf1fb4d 100644 --- a/docs/narr/vhosting.rst +++ b/docs/narr/vhosting.rst @@ -101,8 +101,8 @@ the WSGI environ named ``HTTP_X_VHM_ROOT`` with a value that is the absolute pathname to the resource object in the resource tree that should behave as the "root" resource. As a result, the traversal machinery will respect this value during traversal (prepending it to the PATH_INFO before traversal -starts), and the :func:`pyramid.url.resource_url` API will generate the -"correct" virtually-rooted URLs. +starts), and the :meth:`pyramid.request.Request.resource_url` API will +generate the "correct" virtually-rooted URLs. An example of an Apache ``mod_proxy`` configuration that will host the ``/cms`` subobject as ``http://www.example.com/`` using this facility diff --git a/pyramid/config.py b/pyramid/config.py index c68648a5f..fc00e2ffa 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -1881,13 +1881,13 @@ class Configurator(object): pregenerator This option should be a callable object that implements the - :class:`pyramid.interfaces.IRoutePregenerator` - interface. A :term:`pregenerator` is a callable called by - the :mod:`pyramid.url.route_url` function to augment or - replace the arguments it is passed when generating a URL - for the route. This is a feature not often used directly - by applications, it is meant to be hooked by frameworks - that use :app:`Pyramid` as a base. + :class:`pyramid.interfaces.IRoutePregenerator` interface. A + :term:`pregenerator` is a callable called by the + :meth:`pyramid.request.Request.route_url` function to augment or + replace the arguments it is passed when generating a URL for the + route. This is a feature not often used directly by applications, + it is meant to be hooked by frameworks that use :app:`Pyramid` as + a base. use_global_views @@ -2637,50 +2637,51 @@ class Configurator(object): *Usage* The ``add_static_view`` function is typically used in conjunction - with the :func:`pyramid.url.static_url` function. + with the :meth:`pyramid.request.Request.static_url` method. ``add_static_view`` adds a view which renders a static asset when - some URL is visited; :func:`pyramid.url.static_url` generates a URL - to that asset. + some URL is visited; :meth:`pyramid.request.Request.static_url` + generates a URL to that asset. The ``name`` argument to ``add_static_view`` is usually a :term:`view - name`. When this is the case, the :func:`pyramid.url.static_url` API - will generate a URL which points to a Pyramid view, which will serve - up a set of assets that live in the package itself. For example: + name`. When this is the case, the + :meth:`pyramid.request.Request.static_url` API will generate a URL + which points to a Pyramid view, which will serve up a set of assets + that live in the package itself. For example: .. code-block:: python add_static_view('images', 'mypackage:images/') Code that registers such a view can generate URLs to the view via - :func:`pyramid.url.static_url`: + :meth:`pyramid.request.Request.static_url`: .. code-block:: python - static_url('mypackage:images/logo.png', request) + request.static_url('mypackage:images/logo.png') When ``add_static_view`` is called with a ``name`` argument that represents a URL prefix, as it is above, subsequent calls to - :func:`pyramid.url.static_url` with paths that start with the - ``path`` argument passed to ``add_static_view`` will generate a URL - something like ``http:///images/logo.png``, which - will cause the ``logo.png`` file in the ``images`` subdirectory of - the ``mypackage`` package to be served. + :meth:`pyramid.request.Request.static_url` with paths that start with + the ``path`` argument passed to ``add_static_view`` will generate a + URL something like ``http:///images/logo.png``, + which will cause the ``logo.png`` file in the ``images`` subdirectory + of the ``mypackage`` package to be served. ``add_static_view`` can alternately be used with a ``name`` argument which is a *URL*, causing static assets to be served from an external webserver. This happens when the ``name`` argument is a fully qualified URL (e.g. starts with ``http://`` or similar). In this mode, the ``name`` is used as the prefix of the full URL when - generating a URL using :func:`pyramid.url.static_url`. For example, - if ``add_static_view`` is called like so: + generating a URL using :meth:`pyramid.request.Request.static_url`. + For example, if ``add_static_view`` is called like so: .. code-block:: python add_static_view('http://example.com/images', 'mypackage:images/') - Subsequently, the URLs generated by :func:`pyramid.url.static_url` - for that static view will be prefixed with - ``http://example.com/images``: + Subsequently, the URLs generated by + :meth:`pyramid.request.Request.static_url` for that static view will + be prefixed with ``http://example.com/images``: .. code-block:: python @@ -2688,9 +2689,9 @@ class Configurator(object): When ``add_static_view`` is called with a ``name`` argument that is the URL ``http://example.com/images``, subsequent calls to - :func:`pyramid.url.static_url` with paths that start with the - ``path`` argument passed to ``add_static_view`` will generate a URL - something like ``http://example.com/logo.png``. The external + :meth:`pyramid.request.Request.static_url` with paths that start with + the ``path`` argument passed to ``add_static_view`` will generate a + URL something like ``http://example.com/logo.png``. The external webserver listening on ``example.com`` must be itself configured to respond properly to such a request. diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 61462bb5e..4055db08a 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -634,12 +634,13 @@ ILogger = IDebugLogger # b/c class IRoutePregenerator(Interface): def __call__(request, elements, kw): - """ A pregenerator is a function associated by a developer - with a :term:`route`. The pregenerator for a route is called - by :func:`pyramid.url.route_url` in order to adjust the set - of arguments passed to it by the user for special purposes, - such as Pylons 'subdomain' support. It will influence the URL - returned by ``route_url``. + + """ A pregenerator is a function associated by a developer with a + :term:`route`. The pregenerator for a route is called by + :meth:`pyramid.request.Request.route_url` in order to adjust the set + of arguments passed to it by the user for special purposes, such as + Pylons 'subdomain' support. It will influence the URL returned by + ``route_url``. A pregenerator should return a two-tuple of ``(elements, kw)`` after examining the originals passed to this function, which diff --git a/pyramid/request.py b/pyramid/request.py index 2a654d218..640b0bd97 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -14,10 +14,7 @@ from pyramid.compat import json from pyramid.exceptions import ConfigurationError from pyramid.decorator import reify from pyramid.response import Response -from pyramid.url import resource_url -from pyramid.url import route_url -from pyramid.url import static_url -from pyramid.url import route_path +from pyramid.url import URLMethodsMixin class TemplateContext(object): pass @@ -179,7 +176,7 @@ class DeprecatedRequestMethods(object): rr_dep % ('cache_for', 'cache_expires')) -class Request(BaseRequest, DeprecatedRequestMethods): +class Request(BaseRequest, DeprecatedRequestMethods, URLMethodsMixin): """ A subclass of the :term:`WebOb` Request class. An instance of this class is created by the :term:`router` and is provided to a @@ -331,137 +328,6 @@ class Request(BaseRequest, DeprecatedRequestMethods): '(see the Sessions chapter of the Pyramid documentation)') return factory(self) - def route_url(self, route_name, *elements, **kw): - """ Return the URL for the route named ``route_name``, using - ``*elements`` and ``**kw`` as modifiers. - - This is a convenience method. The result of calling - :meth:`pyramid.request.Request.route_url` is the same as calling - :func:`pyramid.url.route_url` with an explicit ``request`` - parameter. - - The :meth:`pyramid.request.Request.route_url` method calls the - :func:`pyramid.url.route_url` function using the Request object as - the ``request`` argument. The ``route_name``, ``*elements`` and - ``*kw`` arguments passed to :meth:`pyramid.request.Request.route_url` - are passed through to :func:`pyramid.url.route_url` unchanged and its - result is returned. - - This call to :meth:`pyramid.request.Request.route_url`:: - - request.route_url('route_name') - - Is completely equivalent to calling :func:`pyramid.url.route_url` - like this:: - - from pyramid.url import route_url - route_url('route_name', request) - """ - return route_url(route_name, self, *elements, **kw) - - def resource_url(self, resource, *elements, **kw): - """ Return the URL for the :term:`resource` object named ``resource``, - using ``*elements`` and ``**kw`` as modifiers. - - This is a convenience method. The result of calling - :meth:`pyramid.request.Request.resource_url` is the same as calling - :func:`pyramid.url.resource_url` with an explicit ``request`` parameter. - - The :meth:`pyramid.request.Request.resource_url` method calls the - :func:`pyramid.url.resource_url` function using the Request object as - the ``request`` argument. The ``resource``, ``*elements`` and ``*kw`` - arguments passed to :meth:`pyramid.request.Request.resource_url` are - passed through to :func:`pyramid.url.resource_url` unchanged and its - result is returned. - - This call to :meth:`pyramid.request.Request.resource_url`:: - - request.resource_url(myresource) - - Is completely equivalent to calling :func:`pyramid.url.resource_url` - like this:: - - from pyramid.url import resource_url - resource_url(resource, request) - - .. note:: For backwards compatibility purposes, this method can also - be called as :meth:`pyramid.request.Request.model_url`. - """ - return resource_url(resource, self, *elements, **kw) - - model_url = resource_url # b/w compat forever - - def static_url(self, path, **kw): - """ - Generates a fully qualified URL for a static :term:`asset`. The - asset must live within a location defined via the - :meth:`pyramid.config.Configurator.add_static_view` - :term:`configuration declaration` directive (see - :ref:`static_assets_section`). - - This is a convenience method. The result of calling - :meth:`pyramid.request.Request.static_url` is the same as calling - :func:`pyramid.url.static_url` with an explicit ``request`` parameter. - - The :meth:`pyramid.request.Request.static_url` method calls the - :func:`pyramid.url.static_url` function using the Request object as - the ``request`` argument. The ``*kw`` arguments passed to - :meth:`pyramid.request.Request.static_url` are passed through to - :func:`pyramid.url.static_url` unchanged and its result is returned. - - This call to :meth:`pyramid.request.Request.static_url`:: - - request.static_url('mypackage:static/foo.css') - - Is completely equivalent to calling :func:`pyramid.url.static_url` - like this:: - - from pyramid.url import static_url - static_url('mypackage:static/foo.css', request) - - See :func:`pyramid.url.static_url` for more information - - """ - return static_url(path, self, **kw) - - def route_path(self, route_name, *elements, **kw): - """Generates a path (aka a 'relative URL', a URL minus the host, - scheme, and port) for a named :app:`Pyramid` - :term:`route configuration`. - - This is a convenience method. The result of calling - :meth:`pyramid.request.Request.route_path` is the same as calling - :func:`pyramid.url.route_path` with an explicit ``request`` - parameter. - - This method accepts the same arguments as - :meth:`pyramid.request.Request.route_url` and performs the same duty. - It just omits the host, port, and scheme information in the return - value; only the script name, path, query parameters, and anchor data - are present in the returned string. - - The :meth:`pyramid.request.Request.route_path` method calls the - :func:`pyramid.url.route_path` function using the Request object as - the ``request`` argument. The ``*elements`` and ``*kw`` arguments - passed to :meth:`pyramid.request.Request.route_path` are passed - through to :func:`pyramid.url.route_path` unchanged and its result is - returned. - - This call to :meth:`pyramid.request.Request.route_path`:: - - request.route_path('foobar') - - Is completely equivalent to calling :func:`pyramid.url.route_path` - like this:: - - from pyramid.url import route_path - route_path('foobar', request) - - See :func:`pyramid.url.route_path` for more information - - """ - return route_path(route_name, self, *elements, **kw) - @reify def response(self): """This attribute is actually a "reified" property which returns an @@ -495,7 +361,6 @@ class Request(BaseRequest, DeprecatedRequestMethods): def json_body(self): return json.loads(self.body, encoding=self.charset) - def route_request_iface(name, bases=()): # zope.interface treats the __name__ as the __doc__ and changes __name__ # to None for interfaces that contain spaces if you do not pass a diff --git a/pyramid/testing.py b/pyramid/testing.py index 5b0f37f45..7e5bb50d1 100644 --- a/pyramid/testing.py +++ b/pyramid/testing.py @@ -26,6 +26,7 @@ from pyramid.security import has_permission from pyramid.threadlocal import get_current_registry from pyramid.threadlocal import manager from pyramid.request import DeprecatedRequestMethods +from pyramid.url import URLMethodsMixin _marker = object() @@ -620,7 +621,7 @@ class DummySession(dict): def get_csrf_token(self): return self.get('_csrft_', None) -class DummyRequest(DeprecatedRequestMethods): +class DummyRequest(DeprecatedRequestMethods, URLMethodsMixin): """ A DummyRequest object (incompletely) imitates a :term:`request` object. The ``params``, ``environ``, ``headers``, ``path``, and diff --git a/pyramid/tests/test_testing.py b/pyramid/tests/test_testing.py index 159a88ebd..41a4788ec 100644 --- a/pyramid/tests/test_testing.py +++ b/pyramid/tests/test_testing.py @@ -251,15 +251,16 @@ class Test_registerSubscriber(TestBase): class Test_registerRoute(TestBase): def test_registerRoute(self): - from pyramid.url import route_url + from pyramid.request import Request from pyramid.interfaces import IRoutesMapper from pyramid.testing import registerRoute registerRoute(':pagename', 'home', DummyFactory) mapper = self.registry.getUtility(IRoutesMapper) self.assertEqual(len(mapper.routelist), 1) - request = DummyRequest() - self.assertEqual(route_url('home', request, pagename='abc'), - 'http://example.com/abc') + request = Request.blank('/') + request.registry = self.registry + self.assertEqual(request.route_url('home', pagename='abc'), + 'http://localhost/abc') class Test_registerSettings(TestBase): def test_registerSettings(self): diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index cb8326114..652e5a8d2 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -3,16 +3,20 @@ import unittest from pyramid.testing import setUp from pyramid.testing import tearDown -class ResourceURLTests(unittest.TestCase): +class TestURLMethodsMixin(unittest.TestCase): def setUp(self): - setUp() + self.config = setUp() def tearDown(self): tearDown() - def _callFUT(self, resource, request, *elements, **kw): - from pyramid.url import resource_url - return resource_url(resource, request, *elements, **kw) + def _makeOne(self): + from pyramid.url import URLMethodsMixin + class Request(URLMethodsMixin): + application_url = 'http://example.com:5432' + request = Request() + request.registry = self.config.registry + return request def _registerContextURL(self, reg): from pyramid.interfaces import IContextURL @@ -25,247 +29,233 @@ class ResourceURLTests(unittest.TestCase): reg.registerAdapter(DummyContextURL, (Interface, Interface), IContextURL) - def test_root_default(self): - request = _makeRequest() + def test_resource_url_root_default(self): + request = self._makeOne() self._registerContextURL(request.registry) root = DummyContext() - result = self._callFUT(root, request) + result = request.resource_url(root) self.assertEqual(result, 'http://example.com/context/') - def test_extra_args(self): - request = _makeRequest() + def test_resource_url_extra_args(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, 'this/theotherthing', 'that') + result = request.resource_url(context, 'this/theotherthing', 'that') self.assertEqual( result, 'http://example.com/context/this%2Ftheotherthing/that') - def test_unicode_in_element_names(self): - request = _makeRequest() + def test_resource_url_unicode_in_element_names(self): + request = self._makeOne() self._registerContextURL(request.registry) uc = unicode('La Pe\xc3\xb1a', 'utf-8') context = DummyContext() - result = self._callFUT(context, request, uc) + result = request.resource_url(context, uc) self.assertEqual(result, 'http://example.com/context/La%20Pe%C3%B1a') - def test_at_sign_in_element_names(self): - request = _makeRequest() + def test_resource_url_at_sign_in_element_names(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, '@@myview') + result = request.resource_url(context, '@@myview') self.assertEqual(result, 'http://example.com/context/@@myview') - def test_element_names_url_quoted(self): - request = _makeRequest() + def test_resource_url_element_names_url_quoted(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, 'a b c') + result = request.resource_url(context, 'a b c') self.assertEqual(result, 'http://example.com/context/a%20b%20c') - def test_with_query_dict(self): - request = _makeRequest() + def test_resource_url_with_query_dict(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() uc = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT(context, request, 'a', query={'a':uc}) + result = request.resource_url(context, 'a', query={'a':uc}) self.assertEqual(result, 'http://example.com/context/a?a=La+Pe%C3%B1a') - def test_with_query_seq(self): - request = _makeRequest() + def test_resource_url_with_query_seq(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() uc = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT(context, request, 'a', query=[('a', 'hi there'), + result = request.resource_url(context, 'a', query=[('a', 'hi there'), ('b', uc)]) self.assertEqual(result, 'http://example.com/context/a?a=hi+there&b=La+Pe%C3%B1a') - def test_anchor_is_after_root_when_no_elements(self): - request = _makeRequest() + def test_resource_url_anchor_is_after_root_when_no_elements(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, anchor='a') + result = request.resource_url(context, anchor='a') self.assertEqual(result, 'http://example.com/context/#a') - def test_anchor_is_after_elements_when_no_qs(self): - request = _makeRequest() + def test_resource_url_anchor_is_after_elements_when_no_qs(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, 'a', anchor='b') + result = request.resource_url(context, 'a', anchor='b') self.assertEqual(result, 'http://example.com/context/a#b') - def test_anchor_is_after_qs_when_qs_is_present(self): - request = _makeRequest() + def test_resource_url_anchor_is_after_qs_when_qs_is_present(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, 'a', - query={'b':'c'}, anchor='d') + result = request.resource_url(context, 'a', + query={'b':'c'}, anchor='d') self.assertEqual(result, 'http://example.com/context/a?b=c#d') - def test_anchor_is_encoded_utf8_if_unicode(self): - request = _makeRequest() + def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() uc = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT(context, request, anchor=uc) + result = request.resource_url(context, anchor=uc) self.assertEqual(result, 'http://example.com/context/#La Pe\xc3\xb1a') - def test_anchor_is_not_urlencoded(self): - request = _makeRequest() + def test_resource_url_anchor_is_not_urlencoded(self): + request = self._makeOne() self._registerContextURL(request.registry) context = DummyContext() - result = self._callFUT(context, request, anchor=' /#') + result = request.resource_url(context, anchor=' /#') self.assertEqual(result, 'http://example.com/context/# /#') - def test_no_IContextURL_registered(self): + def test_resource_url_no_IContextURL_registered(self): # falls back to TraversalContextURL root = DummyContext() root.__name__ = '' root.__parent__ = None - request = _makeRequest() + request = self._makeOne() request.environ = {} - result = self._callFUT(root, request) + result = request.resource_url(root) self.assertEqual(result, 'http://example.com:5432/') - def test_no_registry_on_request(self): - from pyramid.threadlocal import get_current_registry - reg = get_current_registry() - request = DummyRequest() - self._registerContextURL(reg) + def test_resource_url_no_registry_on_request(self): + request = self._makeOne() + self._registerContextURL(request.registry) + del request.registry root = DummyContext() - result = self._callFUT(root, request) + result = request.resource_url(root) self.assertEqual(result, 'http://example.com/context/') -class TestRouteUrl(unittest.TestCase): - def setUp(self): - self.config = setUp() - - def tearDown(self): - tearDown() - - def _callFUT(self, *arg, **kw): - from pyramid.url import route_url - return route_url(*arg, **kw) - - def test_with_elements(self): + def test_route_url_with_elements(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'extra1', 'extra2') + result = request.route_url('flub', 'extra1', 'extra2') self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2') - def test_with_elements_path_endswith_slash(self): + def test_route_url_with_elements_path_endswith_slash(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3/')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'extra1', 'extra2') + result = request.route_url('flub', 'extra1', 'extra2') self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2') - def test_no_elements(self): + def test_route_url_no_elements(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, a=1, b=2, c=3, _query={'a':1}, - _anchor=u"foo") + result = request.route_url('flub', a=1, b=2, c=3, _query={'a':1}, + _anchor=u"foo") self.assertEqual(result, 'http://example.com:5432/1/2/3?a=1#foo') - def test_with_anchor_string(self): + def test_route_url_with_anchor_string(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, _anchor="La Pe\xc3\xb1a") + result = request.route_url('flub', _anchor="La Pe\xc3\xb1a") self.assertEqual(result, 'http://example.com:5432/1/2/3#La Pe\xc3\xb1a') - def test_with_anchor_unicode(self): + def test_route_url_with_anchor_unicode(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) anchor = unicode('La Pe\xc3\xb1a', 'utf-8') - result = self._callFUT('flub', request, _anchor=anchor) + result = request.route_url('flub', _anchor=anchor) self.assertEqual(result, 'http://example.com:5432/1/2/3#La Pe\xc3\xb1a') - def test_with_query(self): + def test_route_url_with_query(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, _query={'q':'1'}) + result = request.route_url('flub', _query={'q':'1'}) self.assertEqual(result, 'http://example.com:5432/1/2/3?q=1') - def test_with_app_url(self): + def test_route_url_with_app_url(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, _app_url='http://example2.com') + result = request.route_url('flub', _app_url='http://example2.com') self.assertEqual(result, 'http://example2.com/1/2/3') - def test_it_generation_error(self): + def test_route_url_generation_error(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(raise_exc=KeyError) request.registry.registerUtility(mapper, IRoutesMapper) mapper.raise_exc = KeyError - self.assertRaises(KeyError, self._callFUT, 'flub', request, a=1) + self.assertRaises(KeyError, request.route_url, 'flub', request, a=1) - def test_generate_doesnt_receive_query_or_anchor(self): + def test_route_url_generate_doesnt_receive_query_or_anchor(self): from pyramid.interfaces import IRoutesMapper + request = self._makeOne() route = DummyRoute(result='') mapper = DummyRoutesMapper(route=route) - from zope.component import getSiteManager - sm = getSiteManager() - sm.registerUtility(mapper, IRoutesMapper) - request = DummyRequest() - result = self._callFUT('flub', request, _query=dict(name='some_name')) + request.registry.registerUtility(mapper, IRoutesMapper) + result = request.route_url('flub', _query=dict(name='some_name')) self.assertEqual(route.kw, {}) # shouldnt have anchor/query self.assertEqual(result, 'http://example.com:5432?name=some_name') - def test_with_pregenerator(self): + def test_route_url_with_pregenerator(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() route = DummyRoute(result='/1/2/3') def pregenerator(request, elements, kw): return ('a',), {'_app_url':'http://example2.com'} route.pregenerator = pregenerator mapper = DummyRoutesMapper(route=route) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request) + result = request.route_url('flub') self.assertEqual(result, 'http://example2.com/1/2/3/a') self.assertEqual(route.kw, {}) # shouldnt have anchor/query - def test_with_anchor_app_url_elements_and_query(self): + def test_route_url_with_anchor_app_url_elements_and_query(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute(result='/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'element1', - _app_url='http://example2.com', - _anchor='anchor', _query={'q':'1'}) + result = request.route_url('flub', 'element1', + _app_url='http://example2.com', + _anchor='anchor', _query={'q':'1'}) self.assertEqual(result, 'http://example2.com/1/2/3/element1?q=1#anchor') - def test_integration_with_real_request(self): + def test_route_url_integration_with_real_request(self): # to try to replicate https://github.com/Pylons/pyramid/issues/213 from pyramid.interfaces import IRoutesMapper from pyramid.request import Request @@ -273,147 +263,213 @@ class TestRouteUrl(unittest.TestCase): request.registry = self.config.registry mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'extra1', 'extra2') + result = request.route_url('flub', 'extra1', 'extra2') self.assertEqual(result, 'http://localhost/1/2/3/extra1/extra2') -class TestCurrentRouteUrl(unittest.TestCase): - def setUp(self): - setUp() - - def tearDown(self): - tearDown() - - def _callFUT(self, *arg, **kw): - from pyramid.url import current_route_url - return current_route_url(*arg, **kw) - - def test_current_request_has_no_route(self): - request = _makeRequest() - self.assertRaises(ValueError, self._callFUT, request) + def test_current_route_url_current_request_has_no_route(self): + request = self._makeOne() + self.assertRaises(ValueError, request.current_route_url) - def test_with_elements_query_and_anchor(self): + def test_current_route_url_with_elements_query_and_anchor(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() route = DummyRoute('/1/2/3') mapper = DummyRoutesMapper(route=route) request.matched_route = route request.matchdict = {} request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT(request, 'extra1', 'extra2', _query={'a':1}, - _anchor=u"foo") + result = request.current_route_url('extra1', 'extra2', _query={'a':1}, + _anchor=u"foo") self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') - def test_with__route_name(self): + def test_current_route_url_with_route_name(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() route = DummyRoute('/1/2/3') mapper = DummyRoutesMapper(route=route) request.matched_route = route request.matchdict = {} request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT(request, 'extra1', 'extra2', _query={'a':1}, - _anchor=u"foo", _route_name='bar') + result = request.current_route_url('extra1', 'extra2', _query={'a':1}, + _anchor=u"foo", _route_name='bar') self.assertEqual(result, 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') -class TestRoutePath(unittest.TestCase): - def setUp(self): - setUp() - - def tearDown(self): - tearDown() - - def _callFUT(self, *arg, **kw): - from pyramid.url import route_path - return route_path(*arg, **kw) - - def test_with_elements(self): + def test_route_path_with_elements(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'extra1', 'extra2', - a=1, b=2, c=3, _query={'a':1}, - _anchor=u"foo") + request.script_name = '' + result = request.route_path('flub', 'extra1', 'extra2', + a=1, b=2, c=3, _query={'a':1}, + _anchor=u"foo") self.assertEqual(result, '/1/2/3/extra1/extra2?a=1#foo') - def test_with_script_name(self): + def test_route_path_with_script_name(self): from pyramid.interfaces import IRoutesMapper - request = _makeRequest() + request = self._makeOne() request.script_name = '/foo' mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) request.registry.registerUtility(mapper, IRoutesMapper) - result = self._callFUT('flub', request, 'extra1', 'extra2', - a=1, b=2, c=3, _query={'a':1}, - _anchor=u"foo") + result = request.route_path('flub', 'extra1', 'extra2', + a=1, b=2, c=3, _query={'a':1}, + _anchor=u"foo") self.assertEqual(result, '/foo/1/2/3/extra1/extra2?a=1#foo') -class TestStaticUrl(unittest.TestCase): - def setUp(self): - setUp() - - def tearDown(self): - tearDown() - - def _callFUT(self, *arg, **kw): - from pyramid.url import static_url - return static_url(*arg, **kw) - - def test_staticurlinfo_notfound(self): - request = _makeRequest() - self.assertRaises(ValueError, self._callFUT, 'static/foo.css', request) + def test_static_url_staticurlinfo_notfound(self): + request = self._makeOne() + self.assertRaises(ValueError, request.static_url, 'static/foo.css') - def test_abspath(self): - request = _makeRequest() - self.assertRaises(ValueError, self._callFUT, '/static/foo.css', request) + def test_static_url_abspath(self): + request = self._makeOne() + self.assertRaises(ValueError, request.static_url, '/static/foo.css') - def test_found_rel(self): + def test_static_url_found_rel(self): from pyramid.interfaces import IStaticURLInfo - request = _makeRequest() + request = self._makeOne() info = DummyStaticURLInfo('abc') request.registry.registerUtility(info, IStaticURLInfo) - result = self._callFUT('static/foo.css', request) + result = request.static_url('static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {}) ) - def test_found_abs(self): + def test_static_url_abs(self): from pyramid.interfaces import IStaticURLInfo - request = _makeRequest() + request = self._makeOne() info = DummyStaticURLInfo('abc') request.registry.registerUtility(info, IStaticURLInfo) - result = self._callFUT('pyramid.tests:static/foo.css', request) + result = request.static_url('pyramid.tests:static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {}) ) - def test_found_abs_no_registry_on_request(self): - from pyramid.threadlocal import get_current_registry + def test_static_url_found_abs_no_registry_on_request(self): from pyramid.interfaces import IStaticURLInfo - request = DummyRequest() - registry = get_current_registry() + request = self._makeOne() + registry = request.registry info = DummyStaticURLInfo('abc') registry.registerUtility(info, IStaticURLInfo) - result = self._callFUT('pyramid.tests:static/foo.css', request) + del request.registry + result = request.static_url('pyramid.tests:static/foo.css') self.assertEqual(result, 'abc') self.assertEqual(info.args, ('pyramid.tests:static/foo.css', request, {}) ) +class Test_route_url(unittest.TestCase): + def _callFUT(self, route_name, request, *elements, **kw): + from pyramid.url import route_url + return route_url(route_name, request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def route_url(self, route_name, *elements, **kw): + self.route_name = route_name + self.elements = elements + self.kw = kw + return 'route url' + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, 'a', _app_url='') + self.assertEqual(result, 'route url') + self.assertEqual(request.route_name, 'abc') + self.assertEqual(request.elements, ('a',)) + self.assertEqual(request.kw, {'_app_url':''}) + +class Test_route_path(unittest.TestCase): + def _callFUT(self, route_name, request, *elements, **kw): + from pyramid.url import route_path + return route_path(route_name, request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def route_path(self, route_name, *elements, **kw): + self.route_name = route_name + self.elements = elements + self.kw = kw + return 'route path' + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, 'a', _app_url='') + self.assertEqual(result, 'route path') + self.assertEqual(request.route_name, 'abc') + self.assertEqual(request.elements, ('a',)) + self.assertEqual(request.kw, {'_app_url':''}) + +class Test_resource_url(unittest.TestCase): + def _callFUT(self, resource, request, *elements, **kw): + from pyramid.url import resource_url + return resource_url(resource, request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def resource_url(self, resource, *elements, **kw): + self.resource = resource + self.elements = elements + self.kw = kw + return 'resource url' + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, 'a', _app_url='') + self.assertEqual(result, 'resource url') + self.assertEqual(request.resource, 'abc') + self.assertEqual(request.elements, ('a',)) + self.assertEqual(request.kw, {'_app_url':''}) + +class Test_static_url(unittest.TestCase): + def _callFUT(self, path, request, **kw): + from pyramid.url import static_url + return static_url(path, request, **kw) + + def _makeRequest(self): + class Request(object): + def static_url(self, path, **kw): + self.path = path + self.kw = kw + return 'static url' + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT('abc', request, _app_url='') + self.assertEqual(result, 'static url') + self.assertEqual(request.path, 'abc') + self.assertEqual(request.kw, {'_app_url':''}) + +class Test_current_route_url(unittest.TestCase): + def _callFUT(self, request, *elements, **kw): + from pyramid.url import current_route_url + return current_route_url(request, *elements, **kw) + + def _makeRequest(self): + class Request(object): + def current_route_url(self, *elements, **kw): + self.elements = elements + self.kw = kw + return 'current route url' + return Request() + + def test_it(self): + request = self._makeRequest() + result = self._callFUT(request, 'abc', _app_url='') + self.assertEqual(result, 'current route url') + self.assertEqual(request.elements, ('abc',)) + self.assertEqual(request.kw, {'_app_url':''}) + class DummyContext(object): def __init__(self, next=None): self.next = next -class DummyRequest: - application_url = 'http://example.com:5432' # app_url never ends with slash - script_name = '' - def __init__(self, environ=None): - if environ is None: - environ = {} - self.environ = environ - class DummyRoutesMapper: raise_exc = None def __init__(self, route=None, raise_exc=False): @@ -432,12 +488,6 @@ class DummyRoute: self.kw = kw return self.result -def _makeRequest(environ=None): - from pyramid.registry import Registry - request = DummyRequest(environ) - request.registry = Registry() - return request - class DummyStaticURLInfo: def __init__(self, result): self.result = result diff --git a/pyramid/url.py b/pyramid/url.py index 548d5e9d9..254cd6d0e 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -16,419 +16,481 @@ from pyramid.threadlocal import get_current_registry from pyramid.traversal import TraversalContextURL from pyramid.traversal import quote_path_segment +class URLMethodsMixin(object): + """ Request methods mixin for BaseRequest having to do with URL + generation """ + + def route_url(self, route_name, *elements, **kw): + """Generates a fully qualified URL for a named :app:`Pyramid` + :term:`route configuration`. + + Use the route's ``name`` as the first positional argument. + Additional positional arguments (``*elements``) are appended to the + URL as path segments after it is generated. + + Use keyword arguments to supply values which match any dynamic + path elements in the route definition. Raises a :exc:`KeyError` + exception if the URL cannot be generated for any reason (not + enough arguments, for example). + + For example, if you've defined a route named "foobar" with the path + ``{foo}/{bar}/*traverse``:: + + request.route_url('foobar', + foo='1') => + request.route_url('foobar', + foo='1', + bar='2') => + request.route_url('foobar', + foo='1', + bar='2', + traverse=('a','b')) => http://e.com/1/2/a/b + request.route_url('foobar', + foo='1', + bar='2', + traverse='/a/b') => http://e.com/1/2/a/b + + Values replacing ``:segment`` arguments can be passed as strings + or Unicode objects. They will be encoded to UTF-8 and URL-quoted + before being placed into the generated URL. + + Values replacing ``*remainder`` arguments can be passed as strings + *or* tuples of Unicode/string values. If a tuple is passed as a + ``*remainder`` replacement value, its values are URL-quoted and + encoded to UTF-8. The resulting strings are joined with slashes + and rendered into the URL. If a string is passed as a + ``*remainder`` replacement value, it is tacked on to the URL + untouched. + + If a keyword argument ``_query`` is present, it will be used to + compose a query string that will be tacked on to the end of the + URL. The value of ``_query`` must be a sequence of two-tuples + *or* a data structure with an ``.items()`` method that returns a + sequence of two-tuples (presumably a dictionary). This data + structure will be turned into a query string per the documentation + of :func:`pyramid.encode.urlencode` function. After the query + data is turned into a query string, a leading ``?`` is prepended, + and the resulting string is appended to the generated URL. + + .. note:: Python data structures that are passed as ``_query`` + which are sequences or dictionaries are turned into a + string under the same rules as when run through + :func:`urllib.urlencode` with the ``doseq`` argument + equal to ``True``. This means that sequences can be + passed as values, and a k=v pair will be placed into the + query string for each value. + + If a keyword argument ``_anchor`` is present, its string + representation will be used as a named anchor in the generated URL + (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is + ``http://example.com/route/url``, the resulting generated URL will + be ``http://example.com/route/url#foo``). + + .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 + encoded. If ``_anchor`` is passed as a Unicode object, it + will be converted to UTF-8 before being appended to the + URL. The anchor value is not quoted in any way before + being appended to the generated URL. + + If both ``_anchor`` and ``_query`` are specified, the anchor + element will always follow the query element, + e.g. ``http://example.com?foo=1#bar``. + + If a keyword ``_app_url`` is present, it will be used as the + protocol/hostname/port/leading path prefix of the generated URL. + For example, using an ``_app_url`` of + ``http://example.com:8080/foo`` would cause the URL + ``http://example.com:8080/foo/fleeb/flub`` to be returned from + this function if the expansion of the route pattern associated + with the ``route_name`` expanded to ``/fleeb/flub``. If + ``_app_url`` is not specified, the result of + ``request.application_url`` will be used as the prefix (the + default). + + This function raises a :exc:`KeyError` if the URL cannot be + generated due to missing replacement names. Extra replacement + names are ignored. + + If the route object which matches the ``route_name`` argument has + a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments + arguments passed to this function might be augmented or changed. + """ + try: + reg = self.registry + except AttributeError: + reg = get_current_registry() # b/c + mapper = reg.getUtility(IRoutesMapper) + route = mapper.get_route(route_name) + + if route is None: + raise KeyError('No such route named %s' % route_name) + + if route.pregenerator is not None: + elements, kw = route.pregenerator(self, elements, kw) + + anchor = '' + qs = '' + app_url = None + + if '_query' in kw: + qs = '?' + urlencode(kw.pop('_query'), doseq=True) + + if '_anchor' in kw: + anchor = kw.pop('_anchor') + if isinstance(anchor, unicode): + anchor = anchor.encode('utf-8') + anchor = '#' + anchor + + if '_app_url' in kw: + app_url = kw.pop('_app_url') + + path = route.generate(kw) # raises KeyError if generate fails + + if elements: + suffix = _join_elements(elements) + if not path.endswith('/'): + suffix = '/' + suffix + else: + suffix = '' + + if app_url is None: + # we only defer lookup of application_url until here because + # it's somewhat expensive; we won't need to do it if we've + # been passed _app_url + app_url = self.application_url + + return app_url + path + suffix + qs + anchor + + def route_path(self, route_name, *elements, **kw): + """ + Generates a path (aka a 'relative URL', a URL minus the host, scheme, + and port) for a named :app:`Pyramid` :term:`route configuration`. + + This function accepts the same argument as + :meth:`pyramid.request.Request.route_url` and performs the same duty. + It just omits the host, port, and scheme information in the return + value; only the script_name, path, query parameters, and anchor data + are present in the returned string. + + For example, if you've defined a route named 'foobar' with the path + ``/{foo}/{bar}``, this call to ``route_path``:: + + request.route_path('foobar', foo='1', bar='2') + + Will return the string ``/1/2``. + + .. note:: Calling ``request.route_path('route')`` is the same as + calling ``request.route_url('route', + _app_url=request.script_name)``. + :meth:`pyramid.request.Request.route_path` is, in fact, + implemented in terms of `:meth:`pyramid.request.Request.route_url` + in just this way. As a result, any ``_app_url`` passed within the + ``**kw`` values to ``route_path`` will be ignored. + """ + kw['_app_url'] = self.script_name + return self.route_url(route_name, *elements, **kw) + + def resource_url(self, resource, *elements, **kw): + """ + + Generate a string representing the absolute URL of the + :term:`resource` object based on the ``wsgi.url_scheme``, + ``HTTP_HOST`` or ``SERVER_NAME`` in the request, plus any + ``SCRIPT_NAME``. The overall result of this method is always a + UTF-8 encoded string (never Unicode). + + Examples:: + + request.resource_url(resource) => + + http://example.com/ + + request.resource_url(resource, 'a.html') => + + http://example.com/a.html + + request.resource_url(resource, 'a.html', query={'q':'1'}) => + + http://example.com/a.html?q=1 + + request.resource_url(resource, 'a.html', anchor='abc') => + + http://example.com/a.html#abc + + Any positional arguments passed in as ``elements`` must be strings + Unicode objects, or integer objects. These will be joined by slashes + and appended to the generated resource URL. Each of the elements + passed in is URL-quoted before being appended; if any element is + Unicode, it will converted to a UTF-8 bytestring before being + URL-quoted. If any element is an integer, it will be converted to its + string representation before being URL-quoted. + + .. warning:: if no ``elements`` arguments are specified, the resource + URL will end with a trailing slash. If any + ``elements`` are used, the generated URL will *not* + end in trailing a slash. + + If a keyword argument ``query`` is present, it will be used to + compose a query string that will be tacked on to the end of the URL. + The value of ``query`` must be a sequence of two-tuples *or* a data + structure with an ``.items()`` method that returns a sequence of + two-tuples (presumably a dictionary). This data structure will be + turned into a query string per the documentation of + ``pyramid.url.urlencode`` function. After the query data is turned + into a query string, a leading ``?`` is prepended, and the resulting + string is appended to the generated URL. + + .. note:: Python data structures that are passed as ``query`` which + are sequences or dictionaries are turned into a string + under the same rules as when run through + :func:`urllib.urlencode` with the ``doseq`` argument equal + to ``True``. This means that sequences can be passed as + values, and a k=v pair will be placed into the query string + for each value. + + If a keyword argument ``anchor`` is present, its string + representation will be used as a named anchor in the generated URL + (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is + ``http://example.com/resource/url``, the resulting generated URL will + be ``http://example.com/resource/url#foo``). + + .. note:: If ``anchor`` is passed as a string, it should be UTF-8 + encoded. If ``anchor`` is passed as a Unicode object, it + will be converted to UTF-8 before being appended to the + URL. The anchor value is not quoted in any way before + being appended to the generated URL. + + If both ``anchor`` and ``query`` are specified, the anchor element + will always follow the query element, + e.g. ``http://example.com?foo=1#bar``. + + If the ``resource`` passed in has a ``__resource_url__`` method, it + will be used to generate the URL (scheme, host, port, path) that for + the base resource which is operated upon by this function. See also + :ref:`overriding_resource_url_generation`. + + .. note:: If the :term:`resource` used is the result of a + :term:`traversal`, it must be :term:`location`-aware. The + resource can also be the context of a :term:`URL dispatch`; + contexts found this way do not need to be location-aware. + + .. note:: If a 'virtual root path' is present in the request + environment (the value of the WSGI environ key + ``HTTP_X_VHM_ROOT``), and the resource was obtained via + :term:`traversal`, the URL path will not include the + virtual root prefix (it will be stripped off the left hand + side of the generated URL). + + .. note:: For backwards compatibility purposes, this method is also + aliased as the ``model_url`` method of request. + """ + try: + reg = self.registry + except AttributeError: + reg = get_current_registry() # b/c + + context_url = reg.queryMultiAdapter((resource, self), IContextURL) + if context_url is None: + context_url = TraversalContextURL(resource, self) + resource_url = context_url() + + qs = '' + anchor = '' + + if 'query' in kw: + qs = '?' + urlencode(kw['query'], doseq=True) + + if 'anchor' in kw: + anchor = kw['anchor'] + if isinstance(anchor, unicode): + anchor = anchor.encode('utf-8') + anchor = '#' + anchor + + if elements: + suffix = _join_elements(elements) + else: + suffix = '' + + return resource_url + suffix + qs + anchor + + model_url = resource_url # b/w compat forever + + def static_url(self, path, **kw): + """ + Generates a fully qualified URL for a static :term:`asset`. + The asset must live within a location defined via the + :meth:`pyramid.config.Configurator.add_static_view` + :term:`configuration declaration` (see :ref:`static_assets_section`). + + Example:: + + request.static_url('mypackage:static/foo.css') => + + http://example.com/static/foo.css + + + The ``path`` argument points at a file or directory on disk which + a URL should be generated for. The ``path`` may be either a + relative path (e.g. ``static/foo.css``) or a :term:`asset + specification` (e.g. ``mypackage:static/foo.css``). A ``path`` + may not be an absolute filesystem path (a :exc:`ValueError` will + be raised if this function is supplied with an absolute path). + + The purpose of the ``**kw`` argument is the same as the purpose of + the :meth:`pyramid.request.Request.route_url` ``**kw`` argument. See + the documentation for that function to understand the arguments which + you can provide to it. However, typically, you don't need to pass + anything as ``*kw`` when generating a static asset URL. + + This function raises a :exc:`ValueError` if a static view + definition cannot be found which matches the path specification. + + """ + if os.path.isabs(path): + raise ValueError('Absolute paths cannot be used to generate static ' + 'urls (use a package-relative path or an asset ' + 'specification).') + if not ':' in path: + # if it's not a package:relative/name and it's not an + # /absolute/path it's a relative/path; this means its relative + # to the package in which the caller's module is defined. + package = caller_package() + path = '%s:%s' % (package.__name__, path) + + try: + reg = self.registry + except AttributeError: + reg = get_current_registry() # b/c + + info = reg.queryUtility(IStaticURLInfo) + if info is None: + raise ValueError('No static URL definition matching %s' % path) + + return info.generate(path, self, **kw) + + def current_route_url(self, *elements, **kw): + + """ + Generates a fully qualified URL for a named :app:`Pyramid` + :term:`route configuration` based on the 'current route'. + + This function supplements + :meth:`pyramid.request.Request.route_url`. It presents an easy way to + generate a URL for the 'current route' (defined as the route which + matched when the request was generated). + + The arguments to this method have the same meaning as those with the + same names passed to :meth:`pyramid.request.Request.route_url`. It + also understands an extra argument which ``route_url`` does not named + ``_route_name``. + + The route name used to generate a URL is taken from either the + ``_route_name`` keyword argument or the name of the route which is + currently associated with the request if ``_route_name`` was not + passed. Keys and values from the current request :term:`matchdict` + are combined with the ``kw`` arguments to form a set of defaults + named ``newkw``. Then ``request.route_url(route_name, *elements, + **newkw)`` is called, returning a URL. + + Examples follow. + + If the 'current route' has the route pattern ``/foo/{page}`` and the + current url path is ``/foo/1`` , the matchdict will be + ``{'page':'1'}``. The result of ``request.current_route_url()`` in + this situation will be ``/foo/1``. + + If the 'current route' has the route pattern ``/foo/{page}`` and the + current url path is ``/foo/1``, the matchdict will be + ``{'page':'1'}``. The result of + ``request.current_route_url(page='2')`` in this situation will be + ``/foo/2``. + + Usage of the ``_route_name`` keyword argument: if our routing table + defines routes ``/foo/{action}`` named 'foo' and + ``/foo/{action}/{page}`` named ``fooaction``, and the current url + pattern is ``/foo/view`` (which has matched the ``/foo/{action}`` + route), we may want to use the matchdict args to generate a URL to + the ``fooaction`` route. In this scenario, + ``request.current_route_url(_route_name='fooaction', page='5')`` + Will return string like: ``/foo/view/5``. + + """ + if '_route_name' in kw: + route_name = kw.pop('_route_name') + else: + route = getattr(self, 'matched_route', None) + route_name = getattr(route, 'name', None) + if route_name is None: + raise ValueError('Current request matches no route') + + newkw = {} + newkw.update(self.matchdict) + newkw.update(kw) + return self.route_url(route_name, *elements, **newkw) + def route_url(route_name, request, *elements, **kw): - """Generates a fully qualified URL for a named :app:`Pyramid` - :term:`route configuration`. - - .. note:: Calling :meth:`pyramid.Request.route_url` can be used to - achieve the same result as :func:`pyramid.url.route_url`. - - Use the route's ``name`` as the first positional argument. Use a - request object as the second positional argument. Additional - positional arguments are appended to the URL as path segments - after it is generated. - - Use keyword arguments to supply values which match any dynamic - path elements in the route definition. Raises a :exc:`KeyError` - exception if the URL cannot be generated for any reason (not - enough arguments, for example). - - For example, if you've defined a route named "foobar" with the path - ``{foo}/{bar}/*traverse``:: - - route_url('foobar', request, foo='1') => - route_url('foobar', request, foo='1', bar='2') => - route_url('foobar', request, foo='1', bar='2', - traverse=('a','b')) => http://e.com/1/2/a/b - route_url('foobar', request, foo='1', bar='2', - traverse='/a/b') => http://e.com/1/2/a/b - - Values replacing ``:segment`` arguments can be passed as strings - or Unicode objects. They will be encoded to UTF-8 and URL-quoted - before being placed into the generated URL. - - Values replacing ``*remainder`` arguments can be passed as strings - *or* tuples of Unicode/string values. If a tuple is passed as a - ``*remainder`` replacement value, its values are URL-quoted and - encoded to UTF-8. The resulting strings are joined with slashes - and rendered into the URL. If a string is passed as a - ``*remainder`` replacement value, it is tacked on to the URL - untouched. - - If a keyword argument ``_query`` is present, it will be used to - compose a query string that will be tacked on to the end of the - URL. The value of ``_query`` must be a sequence of two-tuples - *or* a data structure with an ``.items()`` method that returns a - sequence of two-tuples (presumably a dictionary). This data - structure will be turned into a query string per the documentation - of :func:`pyramid.encode.urlencode` function. After the query - data is turned into a query string, a leading ``?`` is prepended, - and the resulting string is appended to the generated URL. - - .. note:: Python data structures that are passed as ``_query`` - which are sequences or dictionaries are turned into a - string under the same rules as when run through - :func:`urllib.urlencode` with the ``doseq`` argument - equal to ``True``. This means that sequences can be - passed as values, and a k=v pair will be placed into the - query string for each value. - - If a keyword argument ``_anchor`` is present, its string - representation will be used as a named anchor in the generated URL - (e.g. if ``_anchor`` is passed as ``foo`` and the route URL is - ``http://example.com/route/url``, the resulting generated URL will - be ``http://example.com/route/url#foo``). - - .. note:: If ``_anchor`` is passed as a string, it should be UTF-8 - encoded. If ``_anchor`` is passed as a Unicode object, it - will be converted to UTF-8 before being appended to the - URL. The anchor value is not quoted in any way before - being appended to the generated URL. - - If both ``_anchor`` and ``_query`` are specified, the anchor - element will always follow the query element, - e.g. ``http://example.com?foo=1#bar``. - - If a keyword ``_app_url`` is present, it will be used as the - protocol/hostname/port/leading path prefix of the generated URL. - For example, using an ``_app_url`` of - ``http://example.com:8080/foo`` would cause the URL - ``http://example.com:8080/foo/fleeb/flub`` to be returned from - this function if the expansion of the route pattern associated - with the ``route_name`` expanded to ``/fleeb/flub``. If - ``_app_url`` is not specified, the result of - ``request.application_url`` will be used as the prefix (the - default). - - This function raises a :exc:`KeyError` if the URL cannot be - generated due to missing replacement names. Extra replacement - names are ignored. - - If the route object which matches the ``route_name`` argument has - a :term:`pregenerator`, the ``*elements`` and ``**kw`` arguments - arguments passed to this function might be augmented or changed. - """ - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - mapper = reg.getUtility(IRoutesMapper) - route = mapper.get_route(route_name) - - if route is None: - raise KeyError('No such route named %s' % route_name) - - if route.pregenerator is not None: - elements, kw = route.pregenerator(request, elements, kw) - - anchor = '' - qs = '' - app_url = None - - if '_query' in kw: - qs = '?' + urlencode(kw.pop('_query'), doseq=True) - - if '_anchor' in kw: - anchor = kw.pop('_anchor') - if isinstance(anchor, unicode): - anchor = anchor.encode('utf-8') - anchor = '#' + anchor - - if '_app_url' in kw: - app_url = kw.pop('_app_url') + This is a backwards compatibility function. Its result is the same as + calling:: - path = route.generate(kw) # raises KeyError if generate fails + request.route_url(route_name, *elements, **kw) - if elements: - suffix = _join_elements(elements) - if not path.endswith('/'): - suffix = '/' + suffix - else: - suffix = '' - - if app_url is None: - # we only defer lookup of application_url until here because - # it's somewhat expensive; we won't need to do it if we've - # been passed _app_url - app_url = request.application_url - - return app_url + path + suffix + qs + anchor + See :meth:`pyramid.request.Request.route_url` for more information. + """ + return request.route_url(route_name, *elements, **kw) def route_path(route_name, request, *elements, **kw): - """Generates a path (aka a 'relative URL', a URL minus the host, scheme, - and port) for a named :app:`Pyramid` :term:`route configuration`. - - .. note:: Calling :meth:`pyramid.Request.route_path` can be used to - achieve the same result as :func:`pyramid.url.route_path`. - - This function accepts the same argument as :func:`pyramid.url.route_url` - and performs the same duty. It just omits the host, port, and scheme - information in the return value; only the script_name, path, - query parameters, and anchor data are present in the returned string. - - For example, if you've defined a route named 'foobar' with the path - ``/{foo}/{bar}``, this call to ``route_path``:: - - route_path('foobar', request, foo='1', bar='2') + """ + This is a backwards compatibility function. Its result is the same as + calling:: - Will return the string ``/1/2``. + request.route_path(route_name, *elements, **kw) - .. note:: Calling ``route_path('route', request)`` is the same as calling - ``route_url('route', request, _app_url=request.script_name)``. - ``route_path`` is, in fact, implemented in terms of ``route_url`` - in just this way. As a result, any ``_app_url`` passed within the - ``**kw`` values to ``route_path`` will be ignored. + See :meth:`pyramid.request.Request.route_path` for more information. """ - kw['_app_url'] = request.script_name - return route_url(route_name, request, *elements, **kw) + return request.route_path(route_name, *elements, **kw) def resource_url(resource, request, *elements, **kw): """ - Generate a string representing the absolute URL of the :term:`resource` - object based on the ``wsgi.url_scheme``, ``HTTP_HOST`` or - ``SERVER_NAME`` in the ``request``, plus any ``SCRIPT_NAME``. The - overall result of this function is always a UTF-8 encoded string - (never Unicode). - - .. note:: Calling :meth:`pyramid.Request.resource_url` can be used to - achieve the same result as :func:`pyramid.url.resource_url`. - - Examples:: - - resource_url(context, request) => - - http://example.com/ - - resource_url(context, request, 'a.html') => - - http://example.com/a.html - - resource_url(context, request, 'a.html', query={'q':'1'}) => - - http://example.com/a.html?q=1 - - resource_url(context, request, 'a.html', anchor='abc') => - - http://example.com/a.html#abc - - Any positional arguments passed in as ``elements`` must be strings - Unicode objects, or integer objects. These will be joined by slashes and - appended to the generated resource URL. Each of the elements passed in - is URL-quoted before being appended; if any element is Unicode, it will - converted to a UTF-8 bytestring before being URL-quoted. If any element - is an integer, it will be converted to its string representation before - being URL-quoted. - - .. warning:: if no ``elements`` arguments are specified, the resource - URL will end with a trailing slash. If any - ``elements`` are used, the generated URL will *not* - end in trailing a slash. - - If a keyword argument ``query`` is present, it will be used to - compose a query string that will be tacked on to the end of the - URL. The value of ``query`` must be a sequence of two-tuples *or* - a data structure with an ``.items()`` method that returns a - sequence of two-tuples (presumably a dictionary). This data - structure will be turned into a query string per the documentation - of ``pyramid.url.urlencode`` function. After the query data is - turned into a query string, a leading ``?`` is prepended, and the - resulting string is appended to the generated URL. - - .. note:: Python data structures that are passed as ``query`` - which are sequences or dictionaries are turned into a - string under the same rules as when run through - :func:`urllib.urlencode` with the ``doseq`` argument - equal to ``True``. This means that sequences can be - passed as values, and a k=v pair will be placed into the - query string for each value. - - If a keyword argument ``anchor`` is present, its string - representation will be used as a named anchor in the generated URL - (e.g. if ``anchor`` is passed as ``foo`` and the resource URL is - ``http://example.com/resource/url``, the resulting generated URL will - be ``http://example.com/resource/url#foo``). - - .. note:: If ``anchor`` is passed as a string, it should be UTF-8 - encoded. If ``anchor`` is passed as a Unicode object, it - will be converted to UTF-8 before being appended to the - URL. The anchor value is not quoted in any way before - being appended to the generated URL. - - If both ``anchor`` and ``query`` are specified, the anchor element - will always follow the query element, - e.g. ``http://example.com?foo=1#bar``. - - If the ``resource`` passed in has a ``__resource_url__`` method, it will - be used to generate the URL (scheme, host, port, path) that for the base - resource which is operated upon by this function. See also - :ref:`overriding_resource_url_generation`. - - .. note:: If the :term:`resource` used is the result of a - :term:`traversal`, it must be :term:`location`-aware. - The resource can also be the context of a :term:`URL - dispatch`; contexts found this way do not need to be - location-aware. - - .. note:: If a 'virtual root path' is present in the request - environment (the value of the WSGI environ key - ``HTTP_X_VHM_ROOT``), and the resource was obtained via - :term:`traversal`, the URL path will not include the - virtual root prefix (it will be stripped off the - left hand side of the generated URL). - - .. note:: For backwards compatibility purposes, this function can also be - imported as ``model_url``, although doing so will emit a deprecation - warning. + This is a backwards compatibility function. Its result is the same as + calling:: + + request.resource_url(resource, *elements, **kw) + + See :meth:`pyramid.request.Request.resource_url` for more information. """ - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - - context_url = reg.queryMultiAdapter((resource, request), IContextURL) - if context_url is None: - context_url = TraversalContextURL(resource, request) - resource_url = context_url() - - qs = '' - anchor = '' - - if 'query' in kw: - qs = '?' + urlencode(kw['query'], doseq=True) - - if 'anchor' in kw: - anchor = kw['anchor'] - if isinstance(anchor, unicode): - anchor = anchor.encode('utf-8') - anchor = '#' + anchor - - if elements: - suffix = _join_elements(elements) - else: - suffix = '' - - return resource_url + suffix + qs + anchor + return request.resource_url(resource, *elements, **kw) model_url = resource_url # b/w compat (forever) deprecated( 'model_url', - 'pyramid.url.model_url is deprecated as of Pyramid 1.0. Use' + 'pyramid.url.model_url is deprecated as of Pyramid 1.0. Use ' '``pyramid.url.resource_url`` instead (API-compat, simple ' - 'rename).') + 'rename) or the ``pyramid.request.Request.resource_url`` method.') def static_url(path, request, **kw): """ - Generates a fully qualified URL for a static :term:`asset`. - The asset must live within a location defined via the - :meth:`pyramid.config.Configurator.add_static_view` - :term:`configuration declaration` (see :ref:`static_assets_section`). - - .. note:: Calling :meth:`pyramid.Request.static_url` can be used to - achieve the same result as :func:`pyramid.url.static_url`. - - Example:: - - static_url('mypackage:static/foo.css', request) => - - http://example.com/static/foo.css - + This is a backwards compatibility function. Its result is the same as + calling:: - The ``path`` argument points at a file or directory on disk which - a URL should be generated for. The ``path`` may be either a - relative path (e.g. ``static/foo.css``) or a :term:`asset - specification` (e.g. ``mypackage:static/foo.css``). A ``path`` - may not be an absolute filesystem path (a :exc:`ValueError` will - be raised if this function is supplied with an absolute path). - - The ``request`` argument should be a :term:`request` object. - - The purpose of the ``**kw`` argument is the same as the purpose of - the :func:`pyramid.url.route_url` ``**kw`` argument. See the - documentation for that function to understand the arguments which - you can provide to it. However, typically, you don't need to pass - anything as ``*kw`` when generating a static asset URL. - - This function raises a :exc:`ValueError` if a static view - definition cannot be found which matches the path specification. + request.static_url(path, **kw) + See :meth:`pyramid.request.Request.static_url` for more information. """ - if os.path.isabs(path): - raise ValueError('Absolute paths cannot be used to generate static ' - 'urls (use a package-relative path or an asset ' - 'specification).') - if not ':' in path: - # if it's not a package:relative/name and it's not an - # /absolute/path it's a relative/path; this means its relative - # to the package in which the caller's module is defined. - package = caller_package() - path = '%s:%s' % (package.__name__, path) - - try: - reg = request.registry - except AttributeError: - reg = get_current_registry() # b/c - - info = reg.queryUtility(IStaticURLInfo) - if info is None: - raise ValueError('No static URL definition matching %s' % path) - - return info.generate(path, request, **kw) + return request.static_url(path, **kw) def current_route_url(request, *elements, **kw): - """Generates a fully qualified URL for a named :app:`Pyramid` - :term:`route configuration` based on the 'current route'. - - This function supplements :func:`pyramid.url.route_url`. It presents an - easy way to generate a URL for the 'current route' (defined as the route - which matched when the request was generated). - - The arguments to this function have the same meaning as those with the - same names passed to :func:`pyramid.url.route_url`. It also understands - an extra argument which ``route_url`` does not named ``_route_name``. - - The route name used to generate a URL is taken from either the - ``_route_name`` keyword argument or the name of the route which is - currently associated with the request if ``_route_name`` was not passed. - Keys and values from the current request :term:`matchdict` are combined - with the ``kw`` arguments to form a set of defaults named ``newkw``. - Then ``route_url(route_name, request, *elements, **newkw)`` is called, - returning a URL. - - Examples follow. - - If the 'current route' has the route pattern ``/foo/{page}`` and the - current url path is ``/foo/1`` , the matchdict will be ``{'page':'1'}``. - The result of ``current_route_url(request)`` in this situation will be - ``/foo/1``. - - If the 'current route' has the route pattern ``/foo/{page}`` and the - current url path is ``/foo/1``, the matchdict will be - ``{'page':'1'}``. The result of ``current_route_url(request, page='2')`` - in this situation will be ``/foo/2``. - - Usage of the ``_route_name`` keyword argument: if our routing table - defines routes ``/foo/{action}`` named 'foo' and ``/foo/{action}/{page}`` - named ``fooaction``, and the current url pattern is ``/foo/view`` (which - has matched the ``/foo/{action}`` route), we may want to use the - matchdict args to generate a URL to the ``fooaction`` route. In this - scenario, ``current_route_url(request, _route_name='fooaction', page='5')`` - Will return string like: ``/foo/view/5``. """ + This is a backwards compatibility function. Its result is the same as + calling:: + + request.current_route_url(*elements, **kw) - if '_route_name' in kw: - route_name = kw.pop('_route_name') - else: - route = getattr(request, 'matched_route', None) - route_name = getattr(route, 'name', None) - if route_name is None: - raise ValueError('Current request matches no route') - - newkw = {} - newkw.update(request.matchdict) - newkw.update(kw) - return route_url(route_name, request, *elements, **newkw) + See :meth:`pyramid.request.Request.current_route_url` for more + information. + """ + return request.current_route_url(*elements, **kw) @lru_cache(1000) def _join_elements(elements): -- cgit v1.2.3