Features
--------
- Add ``pyramid.config.Configurator.add_resource_url_adapter`` API method.
See the Hooks narrative documentation section entitled "Changing How
pyramid.request.Request.resource_url Generates a URL" for more information.
This is not a new feature, it just provides an API for adding a resource
url adapter without needing to use the ZCA API.
- A new interface was added: ``pyramid.interfaces.IResourceURL``. An adapter
implementing its interface can be used to override resource URL generation
when ``request.resource_url`` is called. This interface replaces the
now-deprecated ``pyramid.interfaces.IContextURL`` interface.
- The dictionary passed to a resource's ``__resource_url__`` method (see
"Overriding Resource URL Generation" in the "Resources" chapter) now
contains an ``app_url`` key, representing the application URL generated
during ``request.resource_url``. It represents a potentially customized
URL prefix, containing potentially custom scheme, host and port information
passed by the user to ``request.resource_url``. It should be used instead
of ``request.application_url`` where necessary.
- The ``request.resource_url`` API now accepts these arguments: ``app_url``,
``scheme``, ``host``, and ``port``. The app_url argument can be used to
replace the URL prefix wholesale during url generation. The ``scheme``,
``host``, and ``port`` arguments can be used to replace the respective
default values of ``request.application_url`` partially.
- A new API named ``request.resource_path`` now exists. It works like
``request.resource_url`` but produces a relative URL rather than an
absolute one.
- The ``request.route_url`` API now accepts these arguments: ``_app_url``,
``_scheme``, ``_host``, and ``_port``. The ``_app_url`` argument can be
used to replace the URL prefix wholesale during url generation. The
``_scheme``, ``_host``, and ``_port`` arguments can be used to replace the
respective default values of ``request.application_url`` partially.
Backwards Incompatibilities
---------------------------
- The ``pyramid.interfaces.IContextURL`` interface has been deprecated.
People have been instructed to use this to register a resource url adapter
in the "Hooks" chapter to use to influence ``request.resource_url`` URL
generation for resources found via custom traversers since Pyramid 1.0.
The interface still exists and registering such an adapter still works, but
this interface will be removed from the software after a few major Pyramid
releases. You should replace it with an equivalent
``pyramid.interfaces.IResourceURL`` adapter, registered using the new
``pyramid.config.Configurator.add_resource_url_adapter`` API. A
deprecation warning is now emitted when a
``pyramid.interfaces.IContextURL`` adapter is found when
``request.resource_url`` is called.
Misc
----
- Change ``set_traverser`` API name to ``add_traverser``.
Ref #438.
| | |
| | | something like "AttributeError: 'NoneType' object has no attribute |
| | | 'rfind'". |
| | | |
| | | - Add ``pyramid.config.Configurator.set_traverser`` API method. See the |
| | | - Add ``pyramid.config.Configurator.add_traverser`` API method. See the |
| | | Hooks narrative documentation section entitled "Changing the Traverser" for |
| | | more information. This is not a new feature, it just provides an API for |
| | | adding a traverser without needing to use the ZCA API. |
| | | |
| | | - Add ``pyramid.config.Configurator.add_resource_url_adapter`` API method. |
| | | See the Hooks narrative documentation section entitled "Changing How |
| | | pyramid.request.Request.resource_url Generates a URL" for more information. |
| | | This is not a new feature, it just provides an API for adding a resource |
| | | url adapter without needing to use the ZCA API. |
| | | |
| | | - The system value ``req`` is now supplied to renderers as an alias for |
| | | ``request``. This means that you can now, for example, in a template, do |
| | |
| | | methods and attributes from within templates. The value ``request`` is |
| | | still available too, this is just an alternative. |
| | | |
| | | - A new interface was added: ``pyramid.interfaces.IResourceURL``. An adapter |
| | | implementing its interface can be used to override resource URL generation |
| | | when ``request.resource_url`` is called. This interface replaces the |
| | | now-deprecated ``pyramid.interfaces.IContextURL`` interface. |
| | | |
| | | - The dictionary passed to a resource's ``__resource_url__`` method (see |
| | | "Overriding Resource URL Generation" in the "Resources" chapter) now |
| | | contains an ``app_url`` key, representing the application URL generated |
| | | during ``request.resource_url``. It represents a potentially customized |
| | | URL prefix, containing potentially custom scheme, host and port information |
| | | passed by the user to ``request.resource_url``. It should be used instead |
| | | of ``request.application_url`` where necessary. |
| | | |
| | | - The ``request.resource_url`` API now accepts these arguments: ``app_url``, |
| | | ``scheme``, ``host``, and ``port``. The app_url argument can be used to |
| | | replace the URL prefix wholesale during url generation. The ``scheme``, |
| | | ``host``, and ``port`` arguments can be used to replace the respective |
| | | default values of ``request.application_url`` partially. |
| | | |
| | | - A new API named ``request.resource_path`` now exists. It works like |
| | | ``request.resource_url`` but produces a relative URL rather than an |
| | | absolute one. |
| | | |
| | | - The ``request.route_url`` API now accepts these arguments: ``_app_url``, |
| | | ``_scheme``, ``_host``, and ``_port``. The ``_app_url`` argument can be |
| | | used to replace the URL prefix wholesale during url generation. The |
| | | ``_scheme``, ``_host``, and ``_port`` arguments can be used to replace the |
| | | respective default values of ``request.application_url`` partially. |
| | | |
| | | Backwards Incompatibilities |
| | | --------------------------- |
| | | |
| | | - The ``pyramid.interfaces.IContextURL`` interface has been deprecated. |
| | | People have been instructed to use this to register a resource url adapter |
| | | in the "Hooks" chapter to use to influence ``request.resource_url`` URL |
| | | generation for resources found via custom traversers since Pyramid 1.0. |
| | | |
| | | The interface still exists and registering such an adapter still works, but |
| | | this interface will be removed from the software after a few major Pyramid |
| | | releases. You should replace it with an equivalent |
| | | ``pyramid.interfaces.IResourceURL`` adapter, registered using the new |
| | | ``pyramid.config.Configurator.add_resource_url_adapter`` API. A |
| | | deprecation warning is now emitted when a |
| | | ``pyramid.interfaces.IContextURL`` adapter is found when |
| | | ``request.resource_url`` is called. |
| | | |
| | | Documentation |
| | | ------------- |
| | | |
| | |
| | | |
| | | .. automethod:: set_notfound_view |
| | | |
| | | .. automethod:: set_traverser |
| | | .. automethod:: add_traverser |
| | | |
| | | .. automethod:: set_renderer_globals_factory(factory) |
| | | |
| | |
| | | |
| | | .. autointerface:: IAssetDescriptor |
| | | :members: |
| | | |
| | | .. autointerface:: IResourceURL |
| | | :members: |
| | | |
| | |
| | | |
| | | .. automethod:: resource_url |
| | | |
| | | .. automethod:: resource_path |
| | | |
| | | .. attribute:: response_* |
| | | |
| | | In Pyramid 1.0, you could set attributes on a |
| | |
| | | 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. |
| | | may be incorrect when used against resources derived from your custom |
| | | traverser. |
| | | |
| | | If you've added a traverser, you can change how |
| | | :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: |
| | | type of resource by adding a call to |
| | | :meth:`pyramid.config.add_resource_url_adapter`. |
| | | |
| | | For example: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from pyramid.interfaces import ITraverser |
| | | from zope.interface import Interface |
| | | from myapp.traversal import URLGenerator |
| | | from myapp.traversal import ResourceURLAdapter |
| | | from myapp.resources import MyRoot |
| | | |
| | | config.registry.registerAdapter(URLGenerator, (MyRoot, Interface), |
| | | IContextURL) |
| | | config.add_resource_url_adapter(ResourceURLAdapter, resource_iface=MyRoot) |
| | | |
| | | In the above example, the ``myapp.traversal.URLGenerator`` class will be used |
| | | 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 |
| | | ``zope.interface.Interface``). |
| | | In the above example, the ``myapp.traversal.ResourceURLAdapter`` class will |
| | | be used to provide services to :meth:`~pyramid.request.Request.resource_url` |
| | | any time the :term:`resource` passed to ``resource_url`` is of the class |
| | | ``myapp.resources.MyRoot``. The ``resource_iface`` argument ``MyRoot`` |
| | | represents the type of interface that must be possessed by the resource for |
| | | this resource url factory to be found. If the ``resource_iface`` argument is |
| | | omitted, this resource url adapter will be used for *all* resources. |
| | | |
| | | The API that must be implemented by a class that provides |
| | | :class:`~pyramid.interfaces.IContextURL` is as follows: |
| | | The API that must be implemented by your a class that provides |
| | | :class:`~pyramid.interfaces.IResourceURL` is as follows: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | | |
| | | from zope.interface import Interface |
| | | |
| | | class IContextURL(Interface): |
| | | """ An adapter which deals with URLs related to a context. |
| | | class MyResourceURL(object): |
| | | """ An adapter which provides the virtual and physical paths of a |
| | | resource |
| | | """ |
| | | def __init__(self, context, request): |
| | | """ Accept the context and request """ |
| | | |
| | | def virtual_root(self): |
| | | """ Return the virtual root object related to a request and the |
| | | current context""" |
| | | |
| | | def __call__(self): |
| | | """ Return a URL that points to the context """ |
| | | def __init__(self, resource, request): |
| | | """ Accept the resource and request and set self.physical_path and |
| | | self.virtual_path""" |
| | | self.virtual_path = some_function_of(resource, request) |
| | | self.physical_path = some_other_function_of(resource, request) |
| | | |
| | | The default context URL generator is available for perusal as the class |
| | | :class:`pyramid.traversal.TraversalContextURL` in the `traversal module |
| | | :class:`pyramid.traversal.ResourceURL` in the `traversal module |
| | | <http://github.com/Pylons/pyramid/blob/master/pyramid/traversal.py>`_ of the |
| | | :term:`Pylons` GitHub Pyramid repository. |
| | | |
| | | See :meth:`pyramid.config.add_resource_url_adapter` for more information. |
| | | |
| | | .. index:: |
| | | single: IResponse |
| | | single: special view responses |
| | |
| | | http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for |
| | | more information about how to use the ``ignore`` argument to ``scan``. |
| | | |
| | | - Add :meth:`pyramid.config.Configurator.set_traverser` API method. See |
| | | - Add :meth:`pyramid.config.Configurator.add_traverser` API method. See |
| | | :ref:`changing_the_traverser` for more information. This is not a new |
| | | feature, it just provides an API for adding a traverser without needing to |
| | | use the ZCA API. |
| | | |
| | | - Add :meth:`pyramid.config.Configurator.add_resource_url_adapter` API |
| | | method. See :ref:`changing_resource_url` for more information. This is |
| | | not a new feature, it just provides an API for adding a resource url |
| | | adapter without needing to use the ZCA API. |
| | | |
| | | - The :meth:`pyramid.config.Configurator.scan` method can now be passed an |
| | | ``ignore`` argument, which can be a string, a callable, or a list |
| | | consisting of strings and/or callables. This feature allows submodules, |
| | | subpackages, and global objects from being scanned. See |
| | | http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for |
| | | http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for |
| | | more information about how to use the ``ignore`` argument to ``scan``. |
| | | |
| | | - Better error messages when a view callable returns a value that cannot be |
| | |
| | | purely a change to reduce the amount of typing required to use request |
| | | methods and attributes from within templates. The value ``request`` is |
| | | still available too, this is just an alternative. |
| | | |
| | | - A new interface was added: :class:`pyramid.interfaces.IResourceURL`. An |
| | | adapter implementing its interface can be used to override resource URL |
| | | generation when :meth:`pyramid.request.Request.resource_url` is called. |
| | | This interface replaces the now-deprecated |
| | | ``pyramid.interfaces.IContextURL`` interface. |
| | | |
| | | - The dictionary passed to a resource's ``__resource_url__`` method (see |
| | | :ref:`overriding_resource_url_generation`) now contains an ``app_url`` key, |
| | | representing the application URL generated during |
| | | :meth:`pyramid.request.Request.resource_url`. It represents a potentially |
| | | customized URL prefix, containing potentially custom scheme, host and port |
| | | information passed by the user to ``request.resource_url``. It should be |
| | | used instead of ``request.application_url`` where necessary. |
| | | |
| | | - The :meth:`pyramid.request.Request.resource_url` API now accepts these |
| | | arguments: ``app_url``, ``scheme``, ``host``, and ``port``. The app_url |
| | | argument can be used to replace the URL prefix wholesale during url |
| | | generation. The ``scheme``, ``host``, and ``port`` arguments can be used |
| | | to replace the respective default values of ``request.application_url`` |
| | | partially. |
| | | |
| | | - A new API named :meth:`pyramid.request.Request.resource_path` now exists. |
| | | It works like :meth:`pyramid.request.Request.resource_url`` but produces a |
| | | relative URL rather than an absolute one. |
| | | |
| | | - The :meth:`pyramid.request.Request.route_url` API now accepts these |
| | | arguments: ``_app_url``, ``_scheme``, ``_host``, and ``_port``. The |
| | | ``_app_url`` argument can be used to replace the URL prefix wholesale |
| | | during url generation. The ``_scheme``, ``_host``, and ``_port`` arguments |
| | | can be used to replace the respective default values of |
| | | ``request.application_url`` partially. |
| | | |
| | | Backwards Incompatibilities |
| | | --------------------------- |
| | |
| | | no negative affect because the implementation was broken for dict-based |
| | | arguments. |
| | | |
| | | - The ``pyramid.interfaces.IContextURL`` interface has been deprecated. |
| | | People have been instructed to use this to register a resource url adapter |
| | | in the "Hooks" chapter to use to influence |
| | | :meth:`pyramid.request.Request.resource_url` URL generation for resources |
| | | found via custom traversers since Pyramid 1.0. |
| | | |
| | | The interface still exists and registering such an adapter still works, but |
| | | this interface will be removed from the software after a few major Pyramid |
| | | releases. You should replace it with an equivalent |
| | | :class:`pyramid.interfaces.IResourceURL` adapter, registered using the new |
| | | :meth:`pyramid.config.Configurator.add_resource_url_adapter` API. A |
| | | deprecation warning is now emitted when a |
| | | ``pyramid.interfaces.IContextURL`` adapter is found when |
| | | :meth:`pyramid.request.Request.resource_url` is called. |
| | | |
| | | Documentation Enhancements |
| | | -------------------------- |
| | | |
| | |
| | | IRootFactory, |
| | | ISessionFactory, |
| | | ITraverser, |
| | | IResourceURL, |
| | | ) |
| | | |
| | | from pyramid.traversal import DefaultRootFactory |
| | |
| | | self.action(('request properties', name), register, |
| | | introspectables=(intr,)) |
| | | |
| | | def set_traverser(self, factory, iface=None): |
| | | @action_method |
| | | def add_traverser(self, factory, iface=None): |
| | | """ |
| | | The superdefault :term:`traversal` algorithm that :app:`Pyramid` uses |
| | | is explained in :ref:`traversal_algorithm`. Though it is rarely |
| | |
| | | .. code-block:: python |
| | | |
| | | from myapp.traversal import MyCustomTraverser |
| | | config.set_traverser(MyCustomTraverser) |
| | | config.add_traverser(MyCustomTraverser) |
| | | |
| | | This would cause the Pyramid superdefault traverser to never be used; |
| | | intead all traversal would be done using your ``MyCustomTraverser`` |
| | |
| | | |
| | | .. code-block:: python |
| | | |
| | | config.set_traverser(MyCustomTraverser, MyRootClass) |
| | | config.add_traverser(MyCustomTraverser, MyRootClass) |
| | | |
| | | When more than one traverser is active, the "most specific" traverser |
| | | will be used (the one that matches the class or interface of the |
| | |
| | | ) |
| | | intr['factory'] = factory |
| | | intr['iface'] = iface |
| | | self.action(('traverser', iface), register, introspectables=(intr,)) |
| | | self.action(discriminator, register, introspectables=(intr,)) |
| | | |
| | | @action_method |
| | | def add_resource_url_adapter(self, factory, resource_iface=None, |
| | | request_iface=None): |
| | | """ |
| | | When you add a traverser as described in |
| | | :ref:`changing_the_traverser`, it's convenient to continue to use the |
| | | :meth:`pyramid.request.Request.resource_url` API. However, since the |
| | | way traversal is done may have been modified, the URLs that |
| | | ``resource_url`` generates by default may be incorrect when resources |
| | | are returned by a custom traverser. |
| | | |
| | | If you've added a traverser, you can change how |
| | | :meth:`~pyramid.request.Request.resource_url` generates a URL for a |
| | | specific type of resource by calling this method. |
| | | |
| | | The ``factory`` argument represents a class that implements the |
| | | :class:`~pyramid.interfaces.IResourceURL` interface. The class |
| | | constructor should accept two arguments in its constructor (the |
| | | resource and the request) and the resulting instance should provide |
| | | the attributes detailed in that interface (``virtual_path`` and |
| | | ``physical_path``, in particular). |
| | | |
| | | The ``resource_iface`` argument represents a class or interface that |
| | | the resource should possess for this url adapter to be used when |
| | | :meth:`pyramid.request.Request.resource_url` looks up a resource url |
| | | adapter. If ``resource_iface`` is not passed, or it is passed as |
| | | ``None``, the adapter will be used for every type of resource. |
| | | |
| | | The ``request_iface`` argument represents a class or interface that |
| | | the request should possess for this url adapter to be used when |
| | | :meth:`pyramid.request.Request.resource_url` looks up a resource url |
| | | adapter. If ``request_iface`` is not epassed, or it is passed as |
| | | ``None``, the adapter will be used for every type of request. |
| | | |
| | | See :ref:`changing_resource_url` for more information. |
| | | |
| | | .. note:: |
| | | |
| | | This API is new in Pyramid 1.3. |
| | | """ |
| | | factory = self.maybe_dotted(factory) |
| | | resource_iface = self.maybe_dotted(resource_iface) |
| | | request_iface = self.maybe_dotted(request_iface) |
| | | def register(resource_iface=resource_iface, |
| | | request_iface=request_iface): |
| | | if resource_iface is None: |
| | | resource_iface = Interface |
| | | if request_iface is None: |
| | | request_iface = Interface |
| | | self.registry.registerAdapter( |
| | | factory, |
| | | (resource_iface, request_iface), |
| | | IResourceURL, |
| | | ) |
| | | discriminator = ('resource url adapter', resource_iface, request_iface) |
| | | intr = self.introspectable( |
| | | 'resource url adapters', |
| | | discriminator, |
| | | 'resource url adapter for resource iface %r, request_iface %r' % ( |
| | | resource_iface, request_iface), |
| | | 'resource url adapter', |
| | | ) |
| | | intr['factory'] = factory |
| | | intr['resource_iface'] = resource_iface |
| | | intr['request_iface'] = request_iface |
| | | self.action(discriminator, register, introspectables=(intr,)) |
| | | |
| | | def _set_request_properties(event): |
| | | request = event.request |
| | |
| | | ``match`` key will be the matchdict or ``None`` if no route |
| | | matched. Static routes will not be considered for matching. """ |
| | | |
| | | class IContextURL(Interface): |
| | | class IResourceURL(Interface): |
| | | virtual_path = Attribute('The virtual url path of the resource.') |
| | | physical_path = Attribute('The physical url path of the resource.') |
| | | |
| | | class IContextURL(IResourceURL): |
| | | """ An adapter which deals with URLs related to a context. |
| | | |
| | | ..warning:: |
| | | |
| | | This interface is deprecated as of Pyramid 1.3 with the introduction of |
| | | IResourceURL. |
| | | """ |
| | | # this class subclasses IResourceURL because request.resource_url looks |
| | | # for IResourceURL via queryAdapter. queryAdapter will find a deprecated |
| | | # IContextURL registration if no registration for IResourceURL exists. |
| | | # In reality, however, IContextURL objects were never required to have |
| | | # the virtual_path or physical_path attributes spelled in IResourceURL. |
| | | # The inheritance relationship is purely to benefit adapter lookup, |
| | | # not to imply an inheritance relationship of interface attributes |
| | | # and methods. |
| | | # |
| | | # Mechanics: |
| | | # |
| | | # class Fudge(object): |
| | | # def __init__(self, one, two): |
| | | # print one, two |
| | | # class Another(object): |
| | | # def __init__(self, one, two): |
| | | # print one, two |
| | | # ob = object() |
| | | # r.registerAdapter(Fudge, (Interface, Interface), IContextURL) |
| | | # print r.queryMultiAdapter((ob, ob), IResourceURL) |
| | | # r.registerAdapter(Another, (Interface, Interface), IResourceURL) |
| | | # print r.queryMultiAdapter((ob, ob), IResourceURL) |
| | | # |
| | | # prints |
| | | # |
| | | # <object object at 0x7fa678f3e2a0> <object object at 0x7fa678f3e2a0> |
| | | # <__main__.Fudge object at 0x1cda890> |
| | | # <object object at 0x7fa678f3e2a0> <object object at 0x7fa678f3e2a0> |
| | | # <__main__.Another object at 0x1cda850> |
| | | |
| | | def virtual_root(): |
| | | """ Return the virtual root related to a request and the |
| | | current context""" |
| | | |
| | | def __call__(): |
| | | """ Return a URL that points to the context """ |
| | | """ Return a URL that points to the context. """ |
| | | |
| | | class IPackageOverrides(Interface): |
| | | """ Utility for pkg_resources overrides """ |
| | |
| | | self.assertEqual(callables, [('foo', foo, False), |
| | | ('bar', foo, True)]) |
| | | |
| | | def test_set_traverser_dotted_names(self): |
| | | def test_add_traverser_dotted_names(self): |
| | | from pyramid.interfaces import ITraverser |
| | | config = self._makeOne(autocommit=True) |
| | | config.set_traverser( |
| | | config.add_traverser( |
| | | 'pyramid.tests.test_config.test_factories.DummyTraverser', |
| | | 'pyramid.tests.test_config.test_factories.DummyIface') |
| | | iface = DummyIface() |
| | |
| | | self.assertEqual(traverser.__class__, DummyTraverser) |
| | | self.assertEqual(traverser.root, iface) |
| | | |
| | | def test_set_traverser_default_iface_means_Interface(self): |
| | | def test_add_traverser_default_iface_means_Interface(self): |
| | | from pyramid.interfaces import ITraverser |
| | | config = self._makeOne(autocommit=True) |
| | | config.set_traverser(DummyTraverser) |
| | | config.add_traverser(DummyTraverser) |
| | | traverser = config.registry.getAdapter(None, ITraverser) |
| | | self.assertEqual(traverser.__class__, DummyTraverser) |
| | | |
| | | def test_set_traverser_nondefault_iface(self): |
| | | def test_add_traverser_nondefault_iface(self): |
| | | from pyramid.interfaces import ITraverser |
| | | config = self._makeOne(autocommit=True) |
| | | config.set_traverser(DummyTraverser, DummyIface) |
| | | config.add_traverser(DummyTraverser, DummyIface) |
| | | iface = DummyIface() |
| | | traverser = config.registry.getAdapter(iface, ITraverser) |
| | | self.assertEqual(traverser.__class__, DummyTraverser) |
| | | self.assertEqual(traverser.root, iface) |
| | | |
| | | def test_set_traverser_introspectables(self): |
| | | def test_add_traverser_introspectables(self): |
| | | config = self._makeOne() |
| | | config.set_traverser(DummyTraverser, DummyIface) |
| | | config.add_traverser(DummyTraverser, DummyIface) |
| | | actions = config.action_state.actions |
| | | self.assertEqual(len(actions), 1) |
| | | intrs = actions[0]['introspectables'] |
| | |
| | | self.assertEqual(intr.category_name, 'traversers') |
| | | self.assertEqual(intr.title, 'traverser for %r' % DummyIface) |
| | | |
| | | def test_add_resource_url_adapter_dotted_names(self): |
| | | from pyramid.interfaces import IResourceURL |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_resource_url_adapter( |
| | | 'pyramid.tests.test_config.test_factories.DummyResourceURL', |
| | | 'pyramid.tests.test_config.test_factories.DummyIface', |
| | | 'pyramid.tests.test_config.test_factories.DummyIface', |
| | | ) |
| | | iface = DummyIface() |
| | | adapter = config.registry.getMultiAdapter((iface, iface), |
| | | IResourceURL) |
| | | self.assertEqual(adapter.__class__, DummyResourceURL) |
| | | self.assertEqual(adapter.resource, iface) |
| | | self.assertEqual(adapter.request, iface) |
| | | |
| | | def test_add_resource_url_default_interfaces_mean_Interface(self): |
| | | from pyramid.interfaces import IResourceURL |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_resource_url_adapter(DummyResourceURL) |
| | | iface = DummyIface() |
| | | adapter = config.registry.getMultiAdapter((iface, iface), |
| | | IResourceURL) |
| | | self.assertEqual(adapter.__class__, DummyResourceURL) |
| | | self.assertEqual(adapter.resource, iface) |
| | | self.assertEqual(adapter.request, iface) |
| | | |
| | | def test_add_resource_url_nodefault_interfaces(self): |
| | | from zope.interface import Interface |
| | | from pyramid.interfaces import IResourceURL |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_resource_url_adapter(DummyResourceURL, DummyIface, |
| | | DummyIface) |
| | | iface = DummyIface() |
| | | adapter = config.registry.getMultiAdapter((iface, iface), |
| | | IResourceURL) |
| | | self.assertEqual(adapter.__class__, DummyResourceURL) |
| | | self.assertEqual(adapter.resource, iface) |
| | | self.assertEqual(adapter.request, iface) |
| | | bad_result = config.registry.queryMultiAdapter( |
| | | (Interface, Interface), |
| | | IResourceURL, |
| | | ) |
| | | self.assertEqual(bad_result, None) |
| | | |
| | | def test_add_resource_url_adapter_introspectables(self): |
| | | config = self._makeOne() |
| | | config.add_resource_url_adapter(DummyResourceURL, DummyIface) |
| | | actions = config.action_state.actions |
| | | self.assertEqual(len(actions), 1) |
| | | intrs = actions[0]['introspectables'] |
| | | self.assertEqual(len(intrs), 1) |
| | | intr = intrs[0] |
| | | self.assertEqual(intr.type_name, 'resource url adapter') |
| | | self.assertEqual(intr.discriminator, |
| | | ('resource url adapter', DummyIface, None)) |
| | | self.assertEqual(intr.category_name, 'resource url adapters') |
| | | self.assertEqual( |
| | | intr.title, |
| | | "resource url adapter for resource iface " |
| | | "<class 'pyramid.tests.test_config.test_factories.DummyIface'>, " |
| | | "request_iface None" |
| | | ) |
| | | |
| | | |
| | | class DummyRequest(object): |
| | | callables = None |
| | | |
| | |
| | | |
| | | class DummyIface(object): |
| | | pass |
| | | |
| | | class DummyResourceURL(object): |
| | | def __init__(self, resource, request): |
| | | self.resource = resource |
| | | self.request = request |
| | | |
| | |
| | | from pyramid.request import Request |
| | | return Request |
| | | |
| | | def _registerContextURL(self): |
| | | from pyramid.interfaces import IContextURL |
| | | def _registerResourceURL(self): |
| | | from pyramid.interfaces import IResourceURL |
| | | from zope.interface import Interface |
| | | class DummyContextURL(object): |
| | | class DummyResourceURL(object): |
| | | def __init__(self, context, request): |
| | | pass |
| | | def __call__(self): |
| | | return 'http://example.com/context/' |
| | | self.physical_path = '/context/' |
| | | self.virtual_path = '/context/' |
| | | self.config.registry.registerAdapter( |
| | | DummyContextURL, (Interface, Interface), |
| | | IContextURL) |
| | | DummyResourceURL, (Interface, Interface), |
| | | IResourceURL) |
| | | |
| | | def test_charset_defaults_to_utf8(self): |
| | | r = self._makeOne({'PATH_INFO':'/'}) |
| | |
| | | self.assertEqual(inst.finished_callbacks, []) |
| | | |
| | | def test_resource_url(self): |
| | | self._registerContextURL() |
| | | inst = self._makeOne({}) |
| | | self._registerResourceURL() |
| | | environ = { |
| | | 'PATH_INFO':'/', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | 'wsgi.url_scheme':'http', |
| | | } |
| | | inst = self._makeOne(environ) |
| | | root = DummyContext() |
| | | result = inst.resource_url(root) |
| | | self.assertEqual(result, 'http://example.com/context/') |
| | |
| | | import unittest |
| | | import warnings |
| | | |
| | | from pyramid.testing import setUp |
| | | from pyramid.testing import tearDown |
| | |
| | | def tearDown(self): |
| | | tearDown() |
| | | |
| | | def _makeOne(self): |
| | | def _makeOne(self, environ=None): |
| | | from pyramid.url import URLMethodsMixin |
| | | if environ is None: |
| | | environ = {} |
| | | class Request(URLMethodsMixin): |
| | | application_url = 'http://example.com:5432' |
| | | request = Request() |
| | | script_name = '' |
| | | def __init__(self, environ): |
| | | self.environ = environ |
| | | request = Request(environ) |
| | | request.registry = self.config.registry |
| | | return request |
| | | |
| | |
| | | reg.registerAdapter(DummyContextURL, (Interface, Interface), |
| | | IContextURL) |
| | | |
| | | def _registerResourceURL(self, reg): |
| | | from pyramid.interfaces import IResourceURL |
| | | from zope.interface import Interface |
| | | class DummyResourceURL(object): |
| | | def __init__(self, context, request): |
| | | self.physical_path = '/context/' |
| | | self.virtual_path = '/context/' |
| | | reg.registerAdapter(DummyResourceURL, (Interface, Interface), |
| | | IResourceURL) |
| | | |
| | | def test_resource_url_root_default(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_url(root) |
| | | self.assertEqual(result, 'http://example.com/context/') |
| | | self.assertEqual(result, 'http://example.com:5432/context/') |
| | | |
| | | def test_resource_url_extra_args(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, 'this/theotherthing', 'that') |
| | | self.assertEqual( |
| | | result, |
| | | 'http://example.com/context/this%2Ftheotherthing/that') |
| | | 'http://example.com:5432/context/this%2Ftheotherthing/that') |
| | | |
| | | def test_resource_url_unicode_in_element_names(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | uc = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | context = DummyContext() |
| | | result = request.resource_url(context, uc) |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/La%20Pe%C3%B1a') |
| | | 'http://example.com:5432/context/La%20Pe%C3%B1a') |
| | | |
| | | def test_resource_url_at_sign_in_element_names(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, '@@myview') |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/@@myview') |
| | | 'http://example.com:5432/context/@@myview') |
| | | |
| | | def test_resource_url_element_names_url_quoted(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, 'a b c') |
| | | self.assertEqual(result, 'http://example.com/context/a%20b%20c') |
| | | self.assertEqual(result, 'http://example.com:5432/context/a%20b%20c') |
| | | |
| | | def test_resource_url_with_query_dict(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | uc = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | result = request.resource_url(context, 'a', query={'a':uc}) |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/a?a=La+Pe%C3%B1a') |
| | | 'http://example.com:5432/context/a?a=La+Pe%C3%B1a') |
| | | |
| | | def test_resource_url_with_query_seq(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | uc = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | 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') |
| | | 'http://example.com:5432/context/a?a=hi+there&b=La+Pe%C3%B1a') |
| | | |
| | | def test_resource_url_anchor_is_after_root_when_no_elements(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, anchor='a') |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/#a') |
| | | 'http://example.com:5432/context/#a') |
| | | |
| | | def test_resource_url_anchor_is_after_elements_when_no_qs(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, 'a', anchor='b') |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/a#b') |
| | | 'http://example.com:5432/context/a#b') |
| | | |
| | | def test_resource_url_anchor_is_after_qs_when_qs_is_present(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, 'a', |
| | | query={'b':'c'}, anchor='d') |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/a?b=c#d') |
| | | 'http://example.com:5432/context/a?b=c#d') |
| | | |
| | | def test_resource_url_anchor_is_encoded_utf8_if_unicode(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | uc = text_(b'La Pe\xc3\xb1a', 'utf-8') |
| | | result = request.resource_url(context, anchor=uc) |
| | | self.assertEqual( |
| | | result, |
| | | native_( |
| | | text_(b'http://example.com/context/#La Pe\xc3\xb1a', |
| | | text_(b'http://example.com:5432/context/#La Pe\xc3\xb1a', |
| | | 'utf-8'), |
| | | 'utf-8') |
| | | ) |
| | | |
| | | def test_resource_url_anchor_is_not_urlencoded(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | context = DummyContext() |
| | | result = request.resource_url(context, anchor=' /#') |
| | | self.assertEqual(result, |
| | | 'http://example.com/context/# /#') |
| | | 'http://example.com:5432/context/# /#') |
| | | |
| | | def test_resource_url_no_IContextURL_registered(self): |
| | | # falls back to TraversalContextURL |
| | | def test_resource_url_no_IResourceURL_registered(self): |
| | | # falls back to ResourceURL |
| | | root = DummyContext() |
| | | root.__name__ = '' |
| | | root.__parent__ = None |
| | |
| | | |
| | | def test_resource_url_no_registry_on_request(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | self._registerResourceURL(request.registry) |
| | | del request.registry |
| | | root = DummyContext() |
| | | result = request.resource_url(root) |
| | | self.assertEqual(result, 'http://example.com/context/') |
| | | self.assertEqual(result, 'http://example.com:5432/context/') |
| | | |
| | | def test_resource_url_finds_IContextURL(self): |
| | | request = self._makeOne() |
| | | self._registerContextURL(request.registry) |
| | | root = DummyContext() |
| | | with warnings.catch_warnings(record=True) as w: |
| | | warnings.simplefilter('always') |
| | | result = request.resource_url(root) |
| | | self.assertEqual(len(w), 1) |
| | | self.assertEqual(result, 'http://example.com/context/') |
| | | |
| | | def test_resource_url_with_app_url(self): |
| | | request = self._makeOne() |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_url(root, app_url='http://somewhere.com') |
| | | self.assertEqual(result, 'http://somewhere.com/context/') |
| | | |
| | | def test_resource_url_with_scheme(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'8080', |
| | | 'SERVER_NAME':'example.com', |
| | | } |
| | | request = self._makeOne(environ) |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_url(root, scheme='https') |
| | | self.assertEqual(result, 'https://example.com/context/') |
| | | |
| | | def test_resource_url_with_host(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'8080', |
| | | 'SERVER_NAME':'example.com', |
| | | } |
| | | request = self._makeOne(environ) |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_url(root, host='someotherhost.com') |
| | | self.assertEqual(result, 'http://someotherhost.com:8080/context/') |
| | | |
| | | def test_resource_url_with_port(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'8080', |
| | | 'SERVER_NAME':'example.com', |
| | | } |
| | | request = self._makeOne(environ) |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_url(root, port='8181') |
| | | self.assertEqual(result, 'http://example.com:8181/context/') |
| | | |
| | | def test_resource_url_with_local_url(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'8080', |
| | | 'SERVER_NAME':'example.com', |
| | | } |
| | | request = self._makeOne(environ) |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | def resource_url(req, info): |
| | | self.assertEqual(req, request) |
| | | self.assertEqual(info['virtual_path'], '/context/') |
| | | self.assertEqual(info['physical_path'], '/context/') |
| | | self.assertEqual(info['app_url'], 'http://example.com:5432') |
| | | return 'http://example.com/contextabc/' |
| | | root.__resource_url__ = resource_url |
| | | result = request.resource_url(root) |
| | | self.assertEqual(result, 'http://example.com/contextabc/') |
| | | |
| | | def test_resource_path(self): |
| | | request = self._makeOne() |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_path(root) |
| | | self.assertEqual(result, '/context/') |
| | | |
| | | def test_resource_path_kwarg(self): |
| | | request = self._makeOne() |
| | | self._registerResourceURL(request.registry) |
| | | root = DummyContext() |
| | | result = request.resource_path(root, anchor='abc') |
| | | self.assertEqual(result, '/context/#abc') |
| | | |
| | | def test_route_url_with_elements(self): |
| | | from pyramid.interfaces import IRoutesMapper |
| | | request = self._makeOne() |
| | |
| | | self.assertEqual(result, |
| | | 'http://example2.com/1/2/3') |
| | | |
| | | def test_route_url_with_host(self): |
| | | from pyramid.interfaces import IRoutesMapper |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'5432', |
| | | } |
| | | request = self._makeOne(environ) |
| | | mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) |
| | | request.registry.registerUtility(mapper, IRoutesMapper) |
| | | result = request.route_url('flub', _host='someotherhost.com') |
| | | self.assertEqual(result, |
| | | 'http://someotherhost.com:5432/1/2/3') |
| | | |
| | | def test_route_url_with_port(self): |
| | | from pyramid.interfaces import IRoutesMapper |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'5432', |
| | | 'SERVER_NAME':'example.com', |
| | | } |
| | | request = self._makeOne(environ) |
| | | mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) |
| | | request.registry.registerUtility(mapper, IRoutesMapper) |
| | | result = request.route_url('flub', _port='8080') |
| | | self.assertEqual(result, |
| | | 'http://example.com:8080/1/2/3') |
| | | |
| | | def test_route_url_with_scheme(self): |
| | | from pyramid.interfaces import IRoutesMapper |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_PORT':'5432', |
| | | 'SERVER_NAME':'example.com', |
| | | } |
| | | request = self._makeOne(environ) |
| | | mapper = DummyRoutesMapper(route=DummyRoute('/1/2/3')) |
| | | request.registry.registerUtility(mapper, IRoutesMapper) |
| | | result = request.route_url('flub', _scheme='https') |
| | | self.assertEqual(result, |
| | | 'https://example.com/1/2/3') |
| | | |
| | | def test_route_url_generation_error(self): |
| | | from pyramid.interfaces import IRoutesMapper |
| | | request = self._makeOne() |
| | |
| | | {'_app_url':'/foo'}) |
| | | ) |
| | | |
| | | def test_partial_application_url_with_http_host_default_port_http(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'HTTP_HOST':'example.com:80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'http://example.com') |
| | | |
| | | def test_partial_application_url_with_http_host_default_port_https(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'https', |
| | | 'HTTP_HOST':'example.com:443', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'https://example.com') |
| | | |
| | | def test_partial_application_url_with_http_host_nondefault_port_http(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'HTTP_HOST':'example.com:8080', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'http://example.com:8080') |
| | | |
| | | def test_partial_application_url_with_http_host_nondefault_port_https(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'https', |
| | | 'HTTP_HOST':'example.com:4443', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'https://example.com:4443') |
| | | |
| | | def test_partial_application_url_with_http_host_no_colon(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'HTTP_HOST':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'http://example.com') |
| | | |
| | | def test_partial_application_url_no_http_host(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'http://example.com') |
| | | |
| | | def test_partial_application_replace_port(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(port=8080) |
| | | self.assertEqual(result, 'http://example.com:8080') |
| | | |
| | | def test_partial_application_replace_scheme_https_special_case(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(scheme='https') |
| | | self.assertEqual(result, 'https://example.com') |
| | | |
| | | def test_partial_application_replace_scheme_https_special_case_avoid(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(scheme='https', port='8080') |
| | | self.assertEqual(result, 'https://example.com:8080') |
| | | |
| | | def test_partial_application_replace_scheme_http_special_case(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'https', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'8080', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(scheme='http') |
| | | self.assertEqual(result, 'http://example.com') |
| | | |
| | | def test_partial_application_replace_scheme_http_special_case_avoid(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'https', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'8000', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(scheme='http', port='8080') |
| | | self.assertEqual(result, 'http://example.com:8080') |
| | | |
| | | def test_partial_application_replace_host_no_port(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(host='someotherhost.com') |
| | | self.assertEqual(result, 'http://someotherhost.com') |
| | | |
| | | def test_partial_application_replace_host_with_port(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'8000', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(host='someotherhost.com:8080') |
| | | self.assertEqual(result, 'http://someotherhost.com:8080') |
| | | |
| | | def test_partial_application_replace_host_and_port(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url(host='someotherhost.com:8080', |
| | | port='8000') |
| | | self.assertEqual(result, 'http://someotherhost.com:8000') |
| | | |
| | | def test_partial_application_replace_host_port_and_scheme(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'80', |
| | | } |
| | | request = self._makeOne(environ) |
| | | result = request.partial_application_url( |
| | | host='someotherhost.com:8080', |
| | | port='8000', |
| | | scheme='https', |
| | | ) |
| | | self.assertEqual(result, 'https://someotherhost.com:8000') |
| | | |
| | | def test_partial_application_url_with_custom_script_name(self): |
| | | environ = { |
| | | 'wsgi.url_scheme':'http', |
| | | 'SERVER_NAME':'example.com', |
| | | 'SERVER_PORT':'8000', |
| | | } |
| | | request = self._makeOne(environ) |
| | | request.script_name = '/abc' |
| | | result = request.partial_application_url() |
| | | self.assertEqual(result, 'http://example.com:8000/abc') |
| | | |
| | | class Test_route_url(unittest.TestCase): |
| | | def _callFUT(self, route_name, request, *elements, **kw): |
| | | from pyramid.url import route_url |
| | |
| | | from repoze.lru import lru_cache |
| | | |
| | | from pyramid.interfaces import ( |
| | | IResourceURL, |
| | | IContextURL, |
| | | IRequestFactory, |
| | | ITraverser, |
| | |
| | | |
| | | ModelGraphTraverser = ResourceTreeTraverser # b/w compat, not API, used in wild |
| | | |
| | | @implementer(IContextURL) |
| | | class TraversalContextURL(object): |
| | | """ The IContextURL adapter used to generate URLs for a resource in a |
| | | resource tree""" |
| | | |
| | | @implementer(IResourceURL, IContextURL) |
| | | class ResourceURL(object): |
| | | vroot_varname = VH_ROOT_KEY |
| | | |
| | | def __init__(self, context, request): |
| | | self.context = context |
| | | def __init__(self, resource, request): |
| | | physical_path = resource_path(resource) |
| | | if physical_path != '/': |
| | | physical_path = physical_path + '/' |
| | | |
| | | virtual_path = physical_path |
| | | |
| | | environ = request.environ |
| | | vroot_path = environ.get(self.vroot_varname) |
| | | |
| | | # if the physical path starts with the virtual root path, trim it out |
| | | # of the virtual path |
| | | if vroot_path is not None: |
| | | if physical_path.startswith(vroot_path): |
| | | virtual_path = physical_path[len(vroot_path):] |
| | | |
| | | self.virtual_path = virtual_path |
| | | self.physical_path = physical_path |
| | | self.resource = resource |
| | | self.context = resource # bw compat alias for IContextURL compat |
| | | self.request = request |
| | | |
| | | # IContextURL method (deprecated in 1.3) |
| | | def virtual_root(self): |
| | | environ = self.request.environ |
| | | vroot_varname = self.vroot_varname |
| | |
| | | except AttributeError: |
| | | return find_root(self.context) |
| | | |
| | | # IContextURL method (deprecated in 1.3) |
| | | def __call__(self): |
| | | """ Generate a URL based on the :term:`lineage` of a :term:`resource` |
| | | object that is ``self.context``. If any resource in the context |
| | |
| | | 'virtual root path': the path of the URL generated by this will be |
| | | left-stripped of this virtual root path value. |
| | | """ |
| | | resource = self.context |
| | | physical_path = resource_path(resource) |
| | | if physical_path != '/': |
| | | physical_path = physical_path + '/' |
| | | virtual_path = physical_path |
| | | |
| | | request = self.request |
| | | environ = request.environ |
| | | vroot_varname = self.vroot_varname |
| | | vroot_path = environ.get(vroot_varname) |
| | | |
| | | # if the physical path starts with the virtual root path, trim it out |
| | | # of the virtual path |
| | | if vroot_path is not None: |
| | | if physical_path.startswith(vroot_path): |
| | | virtual_path = physical_path[len(vroot_path):] |
| | | |
| | | local_url = getattr(resource, '__resource_url__', None) |
| | | local_url = getattr(self.context, '__resource_url__', None) |
| | | if local_url is not None: |
| | | result = local_url(request, |
| | | {'virtual_path':virtual_path, |
| | | 'physical_path':physical_path}, |
| | | ) |
| | | result = local_url( |
| | | self.request, |
| | | {'virtual_path':self.virtual_path, |
| | | 'physical_path':self.physical_path}, |
| | | ) |
| | | if result is not None: |
| | | # allow it to punt by returning ``None`` |
| | | return result |
| | | |
| | | app_url = request.application_url # never ends in a slash |
| | | return app_url + virtual_path |
| | | app_url = self.request.application_url # never ends in a slash |
| | | return app_url + self.virtual_path |
| | | |
| | | TraversalContextURL = ResourceURL # bw compat as of 1.3 |
| | | |
| | | @lru_cache(1000) |
| | | def _join_path_tuple(tuple): |
| | |
| | | """ Utility functions for dealing with URLs in pyramid """ |
| | | |
| | | import os |
| | | import warnings |
| | | |
| | | from repoze.lru import lru_cache |
| | | |
| | | from pyramid.interfaces import ( |
| | | IContextURL, |
| | | IResourceURL, |
| | | IRoutesMapper, |
| | | IStaticURLInfo, |
| | | ) |
| | | |
| | | from pyramid.compat import ( |
| | | native_, |
| | | bytes_, |
| | | text_type, |
| | | url_quote, |
| | | ) |
| | | from pyramid.encode import urlencode |
| | | from pyramid.path import caller_package |
| | | from pyramid.threadlocal import get_current_registry |
| | | |
| | | from pyramid.traversal import ( |
| | | TraversalContextURL, |
| | | ResourceURL, |
| | | quote_path_segment, |
| | | ) |
| | | |
| | | PATH_SAFE = '/:@&+$,' # from webob |
| | | |
| | | class URLMethodsMixin(object): |
| | | """ Request methods mixin for BaseRequest having to do with URL |
| | | generation """ |
| | | |
| | | def partial_application_url(self, scheme=None, host=None, port=None): |
| | | """ |
| | | Construct the URL defined by request.application_url, replacing any |
| | | of the default scheme, host, or port portions with user-supplied |
| | | variants. |
| | | |
| | | If ``scheme`` is passed as ``https``, and the ``port`` is *not* |
| | | passed, the ``port`` value is assumed to ``443``. Likewise, if |
| | | ``scheme`` is passed as ``http`` and ``port`` is not passed, the |
| | | ``port`` value is assumed to be ``80``. |
| | | """ |
| | | e = self.environ |
| | | if scheme is None: |
| | | scheme = e['wsgi.url_scheme'] |
| | | else: |
| | | if scheme == 'https': |
| | | if port is None: |
| | | port = '443' |
| | | if scheme == 'http': |
| | | if port is None: |
| | | port = '80' |
| | | url = scheme + '://' |
| | | if port is not None: |
| | | port = str(port) |
| | | if host is None: |
| | | host = e.get('HTTP_HOST') |
| | | if host is None: |
| | | host = e['SERVER_NAME'] |
| | | if port is None: |
| | | if ':' in host: |
| | | host, port = host.split(':', 1) |
| | | else: |
| | | port = e['SERVER_PORT'] |
| | | else: |
| | | if ':' in host: |
| | | host, _ = host.split(':', 1) |
| | | if scheme == 'https': |
| | | if port == '443': |
| | | port = None |
| | | elif scheme == 'http': |
| | | if port == '80': |
| | | port = None |
| | | url += host |
| | | if port: |
| | | url += ':%s' % port |
| | | |
| | | url_encoding = getattr(self, 'url_encoding', 'utf-8') # webob 1.2b3+ |
| | | bscript_name = bytes_(self.script_name, url_encoding) |
| | | return url + url_quote(bscript_name, PATH_SAFE) |
| | | |
| | | def route_url(self, route_name, *elements, **kw): |
| | | """Generates a fully qualified URL for a named :app:`Pyramid` |
| | |
| | | element will always follow the query element, |
| | | e.g. ``http://example.com?foo=1#bar``. |
| | | |
| | | If any of ``_scheme``, ``_host``, or ``_port`` is passed and is |
| | | non-``None``, the provided value will replace the named portion in |
| | | the generated URL. If ``_scheme`` is passed as ``https``, and |
| | | ``_port`` is not passed, the ``_port`` value is assumed to have been |
| | | passed as ``443``. Likewise, if ``_scheme`` is passed as ``http`` |
| | | and ``_port`` is not passed, the ``_port`` value is assumed to have |
| | | been passed as ``80``. To avoid this behavior, always explicitly pass |
| | | ``_port`` whenever you pass ``_scheme``. |
| | | |
| | | 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 |
| | |
| | | ``_app_url`` is not specified, the result of |
| | | ``request.application_url`` will be used as the prefix (the |
| | | default). |
| | | |
| | | If both ``_app_url`` and any of ``_scheme``, ``_host``, or ``_port`` |
| | | are passed, ``_app_url`` takes precedence and any values passed for |
| | | ``_scheme``, ``_host``, and ``_port`` will be ignored. |
| | | |
| | | This function raises a :exc:`KeyError` if the URL cannot be |
| | | generated due to missing replacement names. Extra replacement |
| | |
| | | anchor = '' |
| | | qs = '' |
| | | app_url = None |
| | | host = None |
| | | scheme = None |
| | | port = None |
| | | |
| | | if '_query' in kw: |
| | | qs = '?' + urlencode(kw.pop('_query'), doseq=True) |
| | |
| | | if '_app_url' in kw: |
| | | app_url = kw.pop('_app_url') |
| | | |
| | | if '_host' in kw: |
| | | host = kw.pop('_host') |
| | | |
| | | if '_scheme' in kw: |
| | | scheme = kw.pop('_scheme') |
| | | |
| | | if '_port' in kw: |
| | | port = kw.pop('_port') |
| | | |
| | | if app_url is None: |
| | | if (scheme is not None or host is not None or port is not None): |
| | | app_url = self.partial_application_url(scheme, host, port) |
| | | else: |
| | | app_url = self.application_url |
| | | |
| | | path = route.generate(kw) # raises KeyError if generate fails |
| | | |
| | | if elements: |
| | |
| | | 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 |
| | | |
| | |
| | | :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). |
| | | UTF-8 encoded string. |
| | | |
| | | Examples:: |
| | | |
| | |
| | | request.resource_url(resource, 'a.html', anchor='abc') => |
| | | |
| | | http://example.com/a.html#abc |
| | | |
| | | request.resource_url(resource, app_url='') => |
| | | |
| | | / |
| | | |
| | | Any positional arguments passed in as ``elements`` must be strings |
| | | Unicode objects, or integer objects. These will be joined by slashes |
| | |
| | | will always follow the query element, |
| | | e.g. ``http://example.com?foo=1#bar``. |
| | | |
| | | If any of the keyword arguments ``scheme``, ``host``, or ``port`` is |
| | | passed and is non-``None``, the provided value will replace the named |
| | | portion in the generated URL. For example, if you pass |
| | | ``scheme='https'``, and the URL that would be generated without the |
| | | scheme replacement is ``http://foo.com``, the result will be |
| | | ``https://foo.com``. |
| | | |
| | | If ``scheme`` is passed as ``https``, and an explicit ``port`` is not |
| | | passed, the ``port`` value is assumed to have been passed as ``443``. |
| | | Likewise, if ``scheme`` is passed as ``http`` and ``port`` is not |
| | | passed, the ``port`` value is assumed to have been passed as |
| | | ``80``. To avoid this behavior, always explicitly pass ``port`` |
| | | whenever you pass ``scheme``. |
| | | |
| | | If a keyword argument ``app_url`` is passed and is not ``None``, it |
| | | should be a string that will be used as the port/hostname/initial |
| | | path portion of the generated URL instead of the default request |
| | | application URL. For example, if ``app_url='http://foo'``, then the |
| | | resulting url of a resource that has a path of ``/baz/bar`` will be |
| | | ``http://foo/baz/bar``. If you want to generate completely relative |
| | | URLs with no leading scheme, host, port, or initial path, you can |
| | | pass ``app_url=''`. Passing ``app_url=''` when the resource path is |
| | | ``/baz/bar`` will return ``/baz/bar``. |
| | | |
| | | .. note:: |
| | | |
| | | ``app_url`` is new as of Pyramid 1.3. |
| | | |
| | | If ``app_url`` is passed and any of ``scheme``, ``port``, or ``host`` |
| | | are also passed, ``app_url`` will take precedence and the values |
| | | passed for ``scheme``, ``host``, and/or ``port`` will be ignored. |
| | | |
| | | 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 |
| | |
| | | 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() |
| | | url_adapter = reg.queryMultiAdapter((resource, self), IResourceURL) |
| | | if url_adapter is None: |
| | | url_adapter = ResourceURL(resource, self) |
| | | |
| | | virtual_path = getattr(url_adapter, 'virtual_path', None) |
| | | |
| | | if virtual_path is None: |
| | | # old-style IContextURL adapter (Pyramid 1.2 and previous) |
| | | warnings.warn( |
| | | 'Pyramid is using an IContextURL adapter to generate a ' |
| | | 'resource URL; any "app_url", "host", "port", or "scheme" ' |
| | | 'arguments passed to resource_url are being ignored. To ' |
| | | 'avoid this behavior, as of Pyramid 1.3, register an ' |
| | | 'IResourceURL adapter instead of an IContextURL ' |
| | | 'adapter for the resource type(s). IContextURL adapters ' |
| | | 'will be ignored in a later major release of Pyramid.', |
| | | DeprecationWarning, |
| | | 2) |
| | | |
| | | resource_url = url_adapter() |
| | | |
| | | else: |
| | | # newer-style IResourceURL adapter (Pyramid 1.3 and after) |
| | | app_url = None |
| | | scheme = None |
| | | host = None |
| | | port = None |
| | | |
| | | if 'app_url' in kw: |
| | | app_url = kw['app_url'] |
| | | |
| | | if 'scheme' in kw: |
| | | scheme = kw['scheme'] |
| | | |
| | | if 'host' in kw: |
| | | host = kw['host'] |
| | | |
| | | if 'port' in kw: |
| | | port = kw['port'] |
| | | |
| | | if app_url is None: |
| | | if scheme or host or port: |
| | | app_url = self.partial_application_url(scheme, host, port) |
| | | else: |
| | | app_url = self.application_url |
| | | |
| | | resource_url = None |
| | | local_url = getattr(resource, '__resource_url__', None) |
| | | |
| | | if local_url is not None: |
| | | # the resource handles its own url generation |
| | | d = dict( |
| | | virtual_path = virtual_path, |
| | | physical_path = url_adapter.physical_path, |
| | | app_url = app_url, |
| | | ) |
| | | # allow __resource_url__ to punt by returning None |
| | | resource_url = local_url(self, d) |
| | | |
| | | if resource_url is None: |
| | | # the resource did not handle its own url generation or the |
| | | # __resource_url__ function returned None |
| | | resource_url = app_url + virtual_path |
| | | |
| | | qs = '' |
| | | anchor = '' |
| | |
| | | |
| | | model_url = resource_url # b/w compat forever |
| | | |
| | | def resource_path(self, resource, *elements, **kw): |
| | | """ |
| | | Generates a path (aka a 'relative URL', a URL minus the host, scheme, |
| | | and port) for a :term:`resource`. |
| | | |
| | | This function accepts the same argument as |
| | | :meth:`pyramid.request.Request.resource_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. |
| | | |
| | | .. note:: |
| | | |
| | | Calling ``request.resource_path(resource)`` is the same as calling |
| | | ``request.resource_path(resource, app_url=request.script_name)``. |
| | | :meth:`pyramid.request.Request.resource_path` is, in fact, |
| | | implemented in terms of |
| | | :meth:`pyramid.request.Request.resource_url` in just this way. As |
| | | a result, any ``app_url`` passed within the ``**kw`` values to |
| | | ``route_path`` will be ignored. ``scheme``, ``host``, and |
| | | ``port`` are also ignored. |
| | | """ |
| | | kw['app_url'] = self.script_name |
| | | return self.resource_url(resource, *elements, **kw) |
| | | |
| | | def static_url(self, path, **kw): |
| | | """ |
| | | Generates a fully qualified URL for a static :term:`asset`. |