summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2012-02-18 08:39:33 -0500
committerChris McDonough <chrism@plope.com>2012-02-18 08:39:33 -0500
commite0551cd9e73a20f2180fecb1134e07d5ebb90c68 (patch)
treec763420ef16c6e3f73003fc399494ee404bf1f93
parent20ecd354ab99eb0dec5617115f4288ec9262270c (diff)
downloadpyramid-e0551cd9e73a20f2180fecb1134e07d5ebb90c68.tar.gz
pyramid-e0551cd9e73a20f2180fecb1134e07d5ebb90c68.tar.bz2
pyramid-e0551cd9e73a20f2180fecb1134e07d5ebb90c68.zip
move add_traverser and add_resource_url_adapter to adapters
-rw-r--r--docs/narr/introspector.rst22
-rw-r--r--pyramid/config/adapters.py147
-rw-r--r--pyramid/config/factories.py143
-rw-r--r--pyramid/tests/test_config/test_adapters.py122
-rw-r--r--pyramid/tests/test_config/test_factories.py114
5 files changed, 289 insertions, 259 deletions
diff --git a/docs/narr/introspector.rst b/docs/narr/introspector.rst
index 08cc430f6..d465c47d9 100644
--- a/docs/narr/introspector.rst
+++ b/docs/narr/introspector.rst
@@ -540,10 +540,30 @@ introspectables in categories not described here.
The (resolved) interface or class object that represents the return value
of a root factory that this traverser will be used for.
- ``factory``
+ ``adapter``
The (resolved) traverser class.
+``resource url adapters``
+
+ Each introspectable in the ``resource url adapters`` category represents a
+ call to :meth:`pyramid.config.Configurator.add_resource_url_adapter`; each
+ will have the following data.
+
+ ``adapter``
+
+ The (resolved) resource URL adapter class.
+
+ ``resource_iface``
+
+ The (resolved) interface or class object that represents the resource
+ interface that this url adapter is registered for.
+
+ ``request_iface``
+
+ The (resolved) interface or class object that represents the request
+ interface that this url adapter is registered for.
+
Introspection in the Toolbar
----------------------------
diff --git a/pyramid/config/adapters.py b/pyramid/config/adapters.py
index 04571bec3..b6dfdaf41 100644
--- a/pyramid/config/adapters.py
+++ b/pyramid/config/adapters.py
@@ -1,6 +1,10 @@
from zope.interface import Interface
-from pyramid.interfaces import IResponse
+from pyramid.interfaces import (
+ IResponse,
+ ITraverser,
+ IResourceURL,
+ )
from pyramid.config.util import action_method
@@ -72,3 +76,144 @@ class AdaptersConfiguratorMixin(object):
# cope with WebOb response objects that aren't decorated with IResponse
from webob import Response as WebobResponse
self.registry.registerSelfAdapter((WebobResponse,), IResponse)
+
+ @action_method
+ def add_traverser(self, adapter, iface=None):
+ """
+ The superdefault :term:`traversal` algorithm that :app:`Pyramid` uses
+ is explained in :ref:`traversal_algorithm`. Though it is rarely
+ necessary, this default algorithm can be swapped out selectively for
+ a different traversal pattern via configuration. The section
+ entitled :ref:`changing_the_traverser` details how to create a
+ traverser class.
+
+ For example, to override the superdefault traverser used by Pyramid,
+ you might do something like this:
+
+ .. code-block:: python
+
+ from myapp.traversal import 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``
+ class, no matter which object was returned by the :term:`root
+ factory` of this application. Note that we passed no arguments to
+ the ``iface`` keyword parameter. The default value of ``iface``,
+ ``None`` represents that the registered traverser should be used when
+ no other more specific traverser is available for the object returned
+ by the root factory.
+
+ However, more than one traversal algorithm can be active at the same
+ time. The traverser used can depend on the result of the :term:`root
+ factory`. For instance, if your root factory returns more than one
+ type of object conditionally, you could claim that an alternate
+ traverser adapter should be used agsinst one particular class or
+ interface returned by that root factory. When the root factory
+ returned an object that implemented that class or interface, a custom
+ traverser would be used. Otherwise, the default traverser would be
+ used. The ``iface`` argument represents the class of the object that
+ the root factory might return or an :term:`interface` that the object
+ might implement.
+
+ To use a particular traverser only when the root factory returns a
+ particular class:
+
+ .. code-block:: python
+
+ 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
+ value returned by the root factory most closely).
+
+ Note that either ``adapter`` or ``iface`` can be a :term:`dotted
+ Python name` or a Python object.
+
+ See :ref:`changing_the_traverser` for more information.
+ """
+ iface = self.maybe_dotted(iface)
+ adapter= self.maybe_dotted(adapter)
+ def register(iface=iface):
+ if iface is None:
+ iface = Interface
+ self.registry.registerAdapter(adapter, (iface,), ITraverser)
+ discriminator = ('traverser', iface)
+ intr = self.introspectable(
+ 'traversers',
+ discriminator,
+ 'traverser for %r' % iface,
+ 'traverser',
+ )
+ intr['adapter'] = adapter
+ intr['iface'] = iface
+ self.action(discriminator, register, introspectables=(intr,))
+
+ @action_method
+ def add_resource_url_adapter(self, adapter, 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 ``adapter`` 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.
+ """
+ adapter = self.maybe_dotted(adapter)
+ 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(
+ adapter,
+ (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['adapter'] = adapter
+ intr['resource_iface'] = resource_iface
+ intr['request_iface'] = request_iface
+ self.action(discriminator, register, introspectables=(intr,))
+
+
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
index 76f8d86ed..eb4442e98 100644
--- a/pyramid/config/factories.py
+++ b/pyramid/config/factories.py
@@ -1,5 +1,3 @@
-from zope.interface import Interface
-
from pyramid.config.util import action_method
from pyramid.interfaces import (
@@ -9,8 +7,6 @@ from pyramid.interfaces import (
IRequestProperties,
IRootFactory,
ISessionFactory,
- ITraverser,
- IResourceURL,
)
from pyramid.traversal import DefaultRootFactory
@@ -144,145 +140,6 @@ class FactoriesConfiguratorMixin(object):
self.action(('request properties', name), register,
introspectables=(intr,))
- @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
- necessary, this default algorithm can be swapped out selectively for
- a different traversal pattern via configuration. The section
- entitled :ref:`changing_the_traverser` details how to create a
- traverser class.
-
- For example, to override the superdefault traverser used by Pyramid,
- you might do something like this:
-
- .. code-block:: python
-
- from myapp.traversal import 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``
- class, no matter which object was returned by the :term:`root
- factory` of this application. Note that we passed no arguments to
- the ``iface`` keyword parameter. The default value of ``iface``,
- ``None`` represents that the registered traverser should be used when
- no other more specific traverser is available for the object returned
- by the root factory.
-
- However, more than one traversal algorithm can be active at the same
- time. The traverser used can depend on the result of the :term:`root
- factory`. For instance, if your root factory returns more than one
- type of object conditionally, you could claim that an alternate
- traverser adapter should be used agsinst one particular class or
- interface returned by that root factory. When the root factory
- returned an object that implemented that class or interface, a custom
- traverser would be used. Otherwise, the default traverser would be
- used. The ``iface`` argument represents the class of the object that
- the root factory might return or an :term:`interface` that the object
- might implement.
-
- To use a particular traverser only when the root factory returns a
- particular class:
-
- .. code-block:: python
-
- 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
- value returned by the root factory most closely).
-
- Note that either ``factory`` or ``iface`` can be a :term:`dotted
- Python name` or a Python object.
-
- See :ref:`changing_the_traverser` for more information.
- """
- iface = self.maybe_dotted(iface)
- factory = self.maybe_dotted(factory)
- def register(iface=iface):
- if iface is None:
- iface = Interface
- self.registry.registerAdapter(factory, (iface,), ITraverser)
- discriminator = ('traverser', iface)
- intr = self.introspectable(
- 'traversers',
- discriminator,
- 'traverser for %r' % iface,
- 'traverser',
- )
- intr['factory'] = factory
- intr['iface'] = iface
- 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
plist = request.registry.queryUtility(IRequestProperties)
diff --git a/pyramid/tests/test_config/test_adapters.py b/pyramid/tests/test_config/test_adapters.py
index 84b7119cf..b89571639 100644
--- a/pyramid/tests/test_config/test_adapters.py
+++ b/pyramid/tests/test_config/test_adapters.py
@@ -112,3 +112,125 @@ class AdaptersConfiguratorMixinTests(unittest.TestCase):
result = config.registry.queryAdapter('foo', IResponse)
self.assertTrue(result.body, b'foo')
+ def test_add_traverser_dotted_names(self):
+ from pyramid.interfaces import ITraverser
+ config = self._makeOne(autocommit=True)
+ config.add_traverser(
+ 'pyramid.tests.test_config.test_adapters.DummyTraverser',
+ 'pyramid.tests.test_config.test_adapters.DummyIface')
+ iface = DummyIface()
+ traverser = config.registry.getAdapter(iface, ITraverser)
+ self.assertEqual(traverser.__class__, DummyTraverser)
+ self.assertEqual(traverser.root, iface)
+
+ def test_add_traverser_default_iface_means_Interface(self):
+ from pyramid.interfaces import ITraverser
+ config = self._makeOne(autocommit=True)
+ config.add_traverser(DummyTraverser)
+ traverser = config.registry.getAdapter(None, ITraverser)
+ self.assertEqual(traverser.__class__, DummyTraverser)
+
+ def test_add_traverser_nondefault_iface(self):
+ from pyramid.interfaces import ITraverser
+ config = self._makeOne(autocommit=True)
+ 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_add_traverser_introspectables(self):
+ config = self._makeOne()
+ config.add_traverser(DummyTraverser, 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, 'traverser')
+ self.assertEqual(intr.discriminator, ('traverser', DummyIface))
+ self.assertEqual(intr.category_name, 'traversers')
+ self.assertEqual(intr.title, 'traverser for %r' % DummyIface)
+ self.assertEqual(intr['adapter'], DummyTraverser)
+ self.assertEqual(intr['iface'], 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_adapters.DummyResourceURL',
+ 'pyramid.tests.test_config.test_adapters.DummyIface',
+ 'pyramid.tests.test_config.test_adapters.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_adapters.DummyIface'>, "
+ "request_iface None"
+ )
+ self.assertEqual(intr['adapter'], DummyResourceURL)
+ self.assertEqual(intr['resource_iface'], DummyIface)
+ self.assertEqual(intr['request_iface'], None)
+
+
+class DummyTraverser(object):
+ def __init__(self, root):
+ self.root = root
+
+class DummyIface(object):
+ pass
+
+class DummyResourceURL(object):
+ def __init__(self, resource, request):
+ self.resource = resource
+ self.request = request
+
+
diff --git a/pyramid/tests/test_config/test_factories.py b/pyramid/tests/test_config/test_factories.py
index 5f300a73e..0930f9603 100644
--- a/pyramid/tests/test_config/test_factories.py
+++ b/pyramid/tests/test_config/test_factories.py
@@ -129,108 +129,6 @@ class TestFactoriesMixin(unittest.TestCase):
self.assertEqual(callables, [('foo', foo, False),
('bar', foo, True)])
- def test_add_traverser_dotted_names(self):
- from pyramid.interfaces import ITraverser
- config = self._makeOne(autocommit=True)
- config.add_traverser(
- 'pyramid.tests.test_config.test_factories.DummyTraverser',
- 'pyramid.tests.test_config.test_factories.DummyIface')
- iface = DummyIface()
- traverser = config.registry.getAdapter(iface, ITraverser)
- self.assertEqual(traverser.__class__, DummyTraverser)
- self.assertEqual(traverser.root, iface)
-
- def test_add_traverser_default_iface_means_Interface(self):
- from pyramid.interfaces import ITraverser
- config = self._makeOne(autocommit=True)
- config.add_traverser(DummyTraverser)
- traverser = config.registry.getAdapter(None, ITraverser)
- self.assertEqual(traverser.__class__, DummyTraverser)
-
- def test_add_traverser_nondefault_iface(self):
- from pyramid.interfaces import ITraverser
- config = self._makeOne(autocommit=True)
- 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_add_traverser_introspectables(self):
- config = self._makeOne()
- config.add_traverser(DummyTraverser, 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, 'traverser')
- self.assertEqual(intr.discriminator, ('traverser', DummyIface))
- 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):
@@ -244,15 +142,3 @@ class DummyRequest(object):
self.callables = []
self.callables.append((name, callable, reify))
-class DummyTraverser(object):
- def __init__(self, root):
- self.root = root
-
-class DummyIface(object):
- pass
-
-class DummyResourceURL(object):
- def __init__(self, resource, request):
- self.resource = resource
- self.request = request
-