summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyramid/config/factories.py57
-rw-r--r--pyramid/interfaces.py4
-rw-r--r--pyramid/tests/test_config/test_factories.py75
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))