summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt3
-rw-r--r--pyramid/config/factories.py11
-rw-r--r--pyramid/tests/test_config/test_factories.py24
-rw-r--r--pyramid/tests/test_util.py27
-rw-r--r--pyramid/util.py87
5 files changed, 103 insertions, 49 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 8f8286628..de4714bf2 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -26,3 +26,6 @@ Features
``@view_config(request_method=('GET', 'HEAD', 'POST')``. This is because
HEAD is a variant of GET that omits the body, and WebOb has special support
to return an empty body when a HEAD is used.
+
+- ``config.set_request_properties`` now causes less code to be executed at
+ request construction time.
diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py
index eb4442e98..ccbf3bbe9 100644
--- a/pyramid/config/factories.py
+++ b/pyramid/config/factories.py
@@ -10,6 +10,7 @@ from pyramid.interfaces import (
)
from pyramid.traversal import DefaultRootFactory
+from pyramid.util import InstancePropertyMixin
class FactoriesConfiguratorMixin(object):
@action_method
@@ -118,8 +119,8 @@ class FactoriesConfiguratorMixin(object):
"""
callable = self.maybe_dotted(callable)
- if name is None:
- name = callable.__name__
+ name, callable = InstancePropertyMixin._make_property(
+ callable, name=name, reify=reify)
def register():
plist = self.registry.queryUtility(IRequestProperties)
@@ -130,7 +131,7 @@ class FactoriesConfiguratorMixin(object):
self.registry.registerHandler(_set_request_properties,
(INewRequest,))
- plist.append((name, callable, reify))
+ plist.append((name, callable))
intr = self.introspectable('request properties', name,
self.object_description(callable),
@@ -143,6 +144,4 @@ class FactoriesConfiguratorMixin(object):
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)
+ request._set_properties(plist)
diff --git a/pyramid/tests/test_config/test_factories.py b/pyramid/tests/test_config/test_factories.py
index 0930f9603..1dfeda34c 100644
--- a/pyramid/tests/test_config/test_factories.py
+++ b/pyramid/tests/test_config/test_factories.py
@@ -73,7 +73,7 @@ class TestFactoriesMixin(unittest.TestCase):
callable = lambda x: None
config.set_request_property(callable, name='foo')
plist = config.registry.getUtility(IRequestProperties)
- self.assertEqual(plist, [('foo', callable, False)])
+ self.assertEqual(set(p[0] for p in plist), set(['foo']))
def test_set_request_property_with_unnamed_callable(self):
from pyramid.interfaces import IRequestProperties
@@ -81,7 +81,7 @@ class TestFactoriesMixin(unittest.TestCase):
def foo(self): pass
config.set_request_property(foo, reify=True)
plist = config.registry.getUtility(IRequestProperties)
- self.assertEqual(plist, [('foo', foo, True)])
+ self.assertEqual(set(p[0] for p in plist), set(['foo']))
def test_set_request_property_with_property(self):
from pyramid.interfaces import IRequestProperties
@@ -89,7 +89,7 @@ class TestFactoriesMixin(unittest.TestCase):
callable = property(lambda x: None)
config.set_request_property(callable, name='foo')
plist = config.registry.getUtility(IRequestProperties)
- self.assertEqual(plist, [('foo', callable, False)])
+ self.assertEqual(set(p[0] for p in plist), set(['foo']))
def test_set_multiple_request_properties(self):
from pyramid.interfaces import IRequestProperties
@@ -100,8 +100,7 @@ class TestFactoriesMixin(unittest.TestCase):
config.set_request_property(bar, name='bar')
config.commit()
plist = config.registry.getUtility(IRequestProperties)
- self.assertEqual(plist, [('foo', foo, True),
- ('bar', bar, False)])
+ self.assertEqual(set(p[0] for p in plist), set(['foo', 'bar']))
def test_set_multiple_request_properties_conflict(self):
from pyramid.exceptions import ConfigurationConflictError
@@ -125,20 +124,19 @@ class TestFactoriesMixin(unittest.TestCase):
request = DummyRequest(config.registry)
event = Event()
config.registry.notify(event)
- callables = event.request.callables
- self.assertEqual(callables, [('foo', foo, False),
- ('bar', foo, True)])
+ plist = event.request.plist
+ self.assertEqual(set(p[0] for p in plist), set(['foo', 'bar']))
class DummyRequest(object):
- callables = None
+ plist = 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))
+ def _set_properties(self, properties):
+ if self.plist is None:
+ self.plist = []
+ self.plist.extend(properties)
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index 824ee329f..e83ad5922 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -3,12 +3,12 @@ from pyramid.compat import PY3
class Test_InstancePropertyMixin(unittest.TestCase):
def _makeOne(self):
- cls = self._targetClass()
+ cls = self._getTargetClass()
class Foo(cls):
pass
return Foo()
- def _targetClass(self):
+ def _getTargetClass(self):
from pyramid.util import InstancePropertyMixin
return InstancePropertyMixin
@@ -109,6 +109,29 @@ class Test_InstancePropertyMixin(unittest.TestCase):
foo.set_property(lambda _: 2, name='x', reify=True)
self.assertEqual(1, foo.x)
+ def test__make_property(self):
+ from pyramid.decorator import reify
+ cls = self._getTargetClass()
+ name, fn = cls._make_property(lambda x: 1, name='x', reify=True)
+ self.assertEqual(name, 'x')
+ self.assertTrue(isinstance(fn, reify))
+
+ def test__set_properties_with_iterable(self):
+ foo = self._makeOne()
+ x = foo._make_property(lambda _: 1, name='x', reify=True)
+ y = foo._make_property(lambda _: 2, name='y')
+ foo._set_properties([x, y])
+ self.assertEqual(1, foo.x)
+ self.assertEqual(2, foo.y)
+
+ def test__set_properties_with_dict(self):
+ foo = self._makeOne()
+ x_name, x_fn = foo._make_property(lambda _: 1, name='x', reify=True)
+ y_name, y_fn = foo._make_property(lambda _: 2, name='y')
+ foo._set_properties({x_name: x_fn, y_name: y_fn})
+ self.assertEqual(1, foo.x)
+ self.assertEqual(2, foo.y)
+
class Test_WeakOrderedSet(unittest.TestCase):
def _makeOne(self):
from pyramid.config import WeakOrderedSet
diff --git a/pyramid/util.py b/pyramid/util.py
index cca1872b7..7d5c97814 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -20,6 +20,60 @@ class InstancePropertyMixin(object):
on the class itself.
"""
+ @classmethod
+ def _make_property(cls, callable, name=None, reify=False):
+ """ Convert a callable into one suitable for adding to the
+ instance. This will return a 2-tuple containing the computed
+ (name, property) pair.
+ """
+
+ is_property = isinstance(callable, property)
+ if is_property:
+ fn = callable
+ if name is None:
+ raise ValueError('must specify "name" for a property')
+ if reify:
+ raise ValueError('cannot reify a property')
+ elif name is not None:
+ fn = lambda this: callable(this)
+ fn.__name__ = name
+ fn.__doc__ = callable.__doc__
+ else:
+ name = callable.__name__
+ fn = callable
+ if reify:
+ import pyramid.decorator # avoid circular import
+ fn = pyramid.decorator.reify(fn)
+ elif not is_property:
+ fn = property(fn)
+
+ return name, fn
+
+ def _set_properties(self, properties):
+ """ Create several properties on the instance at once.
+
+ This is a more efficient version of
+ :meth:`pyramid.util.InstancePropertyMixin.set_property` which
+ can accept multiple ``(name, property)`` pairs generated via
+ :meth:`pyramid.util.InstancePropertyMixin._make_property`.
+
+ ``attrs`` is a sequence of 2-tuples *or* a data structure with
+ an ``.items()`` method which returns a sequence of 2-tuples
+ (presumably a dictionary). It will be used used to add several
+ properties to the instance in a manner that is more efficient
+ than simply calling ``set_property`` repeatedly.
+ """
+
+ if hasattr(properties, 'items'):
+ attrs = properties.items()
+ else:
+ attrs = properties
+ attrs = dict(properties)
+
+ parent = self.__class__
+ cls = type(parent.__name__, (parent, object), attrs)
+ self.__class__ = cls
+
def set_property(self, callable, name=None, reify=False):
""" Add a callable or a property descriptor to the instance.
@@ -31,12 +85,11 @@ class InstancePropertyMixin(object):
A property may also be reified via the
:class:`pyramid.decorator.reify` decorator by setting
``reify=True``, allowing the result of the evaluation to be
- cached. Thus the value of the property is only computed once for
- the lifetime of the object.
+ cached. Using this method, the value of the property is only
+ computed once for the lifetime of the object.
``callable`` can either be a callable that accepts the instance
- as
- its single positional parameter, or it can be a property
+ as its single positional parameter, or it can be a property
descriptor.
If the ``callable`` is a property descriptor, the ``name``
@@ -73,30 +126,8 @@ class InstancePropertyMixin(object):
>>> foo.y # notice y keeps the original value
1
"""
-
- is_property = isinstance(callable, property)
- if is_property:
- fn = callable
- if name is None:
- raise ValueError('must specify "name" for a property')
- if reify:
- raise ValueError('cannot reify a property')
- elif name is not None:
- fn = lambda this: callable(this)
- fn.__name__ = name
- fn.__doc__ = callable.__doc__
- else:
- name = callable.__name__
- fn = callable
- if reify:
- import pyramid.decorator
- fn = pyramid.decorator.reify(fn)
- elif not is_property:
- fn = property(fn)
- attrs = { name: fn }
- parent = self.__class__
- cls = type(parent.__name__, (parent, object), attrs)
- self.__class__ = cls
+ prop = self._make_property(callable, name=name, reify=reify)
+ self._set_properties([prop])
class WeakOrderedSet(object):
""" Maintain a set of items.