diff options
| -rw-r--r-- | pyramid/config/factories.py | 57 | ||||
| -rw-r--r-- | pyramid/interfaces.py | 4 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_factories.py | 75 |
3 files changed, 134 insertions, 2 deletions
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index 530b6cc28..ca8d3f199 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -2,7 +2,9 @@ from pyramid.config.util import action_method from pyramid.interfaces import ( IDefaultRootFactory, + INewRequest, IRequestFactory, + IRequestProperties, IRootFactory, ISessionFactory, ) @@ -85,3 +87,58 @@ class FactoriesConfiguratorMixin(object): intr['factory'] = factory self.action(IRequestFactory, register, introspectables=(intr,)) + @action_method + def set_request_property(self, callable, name=None, reify=False): + """ Add a property to the request object. + + ``callable`` can either be a callable that accepts the request + as its single positional parameter, or it can be a property + descriptor. It may also be a :term:`dotted Python name` which + refers to either a callable or a property descriptor. + + If the ``callable`` is a property descriptor a ``ValueError`` + will be raised if ``name`` is ``None`` or ``reify`` is ``True``. + + If ``name`` is None, the name of the property will be computed + from the name of the ``callable``. + + See :meth:`pyramid.request.Request.set_property` for more + information on its usage. + + This is the recommended method for extending the request object + and should be used in favor of providing a custom request + factory via + :meth:`pyramid.config.Configurator.set_request_factory`. + + .. versionadded:: 1.3 + """ + callable = self.maybe_dotted(callable) + + if name is None: + name = callable.__name__ + + def register(): + plist = self.registry.queryUtility(IRequestProperties) + + if plist is None: + plist = [] + self.registry.registerUtility(plist, IRequestProperties) + self.registry.registerHandler(_set_request_properties, + (INewRequest,)) + + plist.append((name, callable, reify)) + + intr = self.introspectable('request properties', name, + self.object_description(callable), + 'request property') + intr['callable'] = callable + intr['reify'] = reify + self.action(('request properties', name), register, + introspectables=(intr,)) + +def _set_request_properties(event): + request = event.request + plist = request.registry.queryUtility(IRequestProperties) + for prop in plist: + name, callable, reify = prop + request.set_property(callable, name=name, reify=reify) diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 6762d788d..8de5331b9 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -511,6 +511,10 @@ class IRequestHandler(Interface): IRequest.combined = IRequest # for exception view lookups +class IRequestProperties(Interface): + """ Marker interface for storing a list of request properties which + will be added to the request object.""" + class IRouteRequest(Interface): """ *internal only* interface used as in a utility lookup to find route-specific interfaces. Not an API.""" diff --git a/pyramid/tests/test_config/test_factories.py b/pyramid/tests/test_config/test_factories.py index 9cd13a435..d1a01568f 100644 --- a/pyramid/tests/test_config/test_factories.py +++ b/pyramid/tests/test_config/test_factories.py @@ -40,7 +40,7 @@ class TestFactoriesMixin(unittest.TestCase): config.commit() self.assertEqual(config.registry.getUtility(IRootFactory), DefaultRootFactory) - + def test_set_root_factory_dottedname(self): from pyramid.interfaces import IRootFactory config = self._makeOne() @@ -48,7 +48,7 @@ class TestFactoriesMixin(unittest.TestCase): self.assertEqual(config.registry.queryUtility(IRootFactory), None) config.commit() self.assertEqual(config.registry.getUtility(IRootFactory), dummyfactory) - + def test_set_session_factory(self): from pyramid.interfaces import ISessionFactory config = self._makeOne() @@ -67,4 +67,75 @@ class TestFactoriesMixin(unittest.TestCase): self.assertEqual(config.registry.getUtility(ISessionFactory), dummyfactory) + def test_set_request_property_with_callable(self): + from pyramid.interfaces import IRequestProperties + config = self._makeOne(autocommit=True) + callable = lambda x: None + config.set_request_property(callable, name='foo') + plist = config.registry.getUtility(IRequestProperties) + self.assertEqual(plist, [('foo', callable, False)]) + + def test_set_request_property_with_unnamed_callable(self): + from pyramid.interfaces import IRequestProperties + config = self._makeOne(autocommit=True) + def foo(self): pass + config.set_request_property(foo, reify=True) + plist = config.registry.getUtility(IRequestProperties) + self.assertEqual(plist, [('foo', foo, True)]) + + def test_set_request_property_with_property(self): + from pyramid.interfaces import IRequestProperties + config = self._makeOne(autocommit=True) + callable = property(lambda x: None) + config.set_request_property(callable, name='foo') + plist = config.registry.getUtility(IRequestProperties) + self.assertEqual(plist, [('foo', callable, False)]) + + def test_set_multiple_request_properties(self): + from pyramid.interfaces import IRequestProperties + config = self._makeOne() + def foo(self): pass + bar = property(lambda x: None) + config.set_request_property(foo, reify=True) + config.set_request_property(bar, name='bar') + config.commit() + plist = config.registry.getUtility(IRequestProperties) + self.assertEqual(plist, [('foo', foo, True), + ('bar', bar, False)]) + + def test_set_multiple_request_properties_conflict(self): + from pyramid.exceptions import ConfigurationConflictError + config = self._makeOne() + def foo(self): pass + bar = property(lambda x: None) + config.set_request_property(foo, name='bar', reify=True) + config.set_request_property(bar, name='bar') + self.assertRaises(ConfigurationConflictError, config.commit) + + def test_set_request_property_subscriber(self): + from zope.interface import implementer + from pyramid.interfaces import INewRequest + config = self._makeOne() + def foo(r): pass + config.set_request_property(foo, name='foo') + config.set_request_property(foo, name='bar', reify=True) + config.commit() + @implementer(INewRequest) + class Event(object): + request = DummyRequest(config.registry) + event = Event() + config.registry.notify(event) + callables = event.request.callables + self.assertEqual(callables, [('foo', foo, False), + ('bar', foo, True)]) + +class DummyRequest(object): + callables = None + + def __init__(self, registry): + self.registry = registry + def set_property(self, callable, name, reify): + if self.callables is None: + self.callables = [] + self.callables.append((name, callable, reify)) |
