Chris McDonough
2012-09-11 73598739fbd0596ef15b15f827d6a47fb5862a98
- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available to tweens.

- Request properties and methods added via ``config.set_request_property`` or
``config.add_request_method`` are now available in the request object
returned from ``pyramid.paster.bootstrap``.

Related partially to issue #520 and issue #538 and issue #596, although it doesn't actually allow us to close any of them, because we still don't issue a newrequest event when bootstrap is used.
9 files modified
173 ■■■■■ changed files
CHANGES.txt 10 ●●●●● patch | view | raw | blame | history
pyramid/config/factories.py 9 ●●●●● patch | view | raw | blame | history
pyramid/router.py 6 ●●●● patch | view | raw | blame | history
pyramid/scripting.py 5 ●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_factories.py 30 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_router.py 32 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_scripting.py 60 ●●●● patch | view | raw | blame | history
pyramid/tests/test_util.py 14 ●●●●● patch | view | raw | blame | history
pyramid/util.py 7 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -147,6 +147,16 @@
- ``pyramid.testing.DummyRequest`` now supports methods supplied by the
  ``pyramid.util.InstancePropertyMixin`` class such as ``set_property``.
- Request properties and methods added via ``config.set_request_property`` or
  ``config.add_request_method`` are now available to tweens.
- Request properties and methods added via ``config.set_request_property`` or
  ``config.add_request_method`` are now available to tweens.
- Request properties and methods added via ``config.set_request_property`` or
  ``config.add_request_method`` are now available in the request object
  returned from ``pyramid.paster.bootstrap``.
Deprecations
------------
pyramid/config/factories.py
@@ -153,8 +153,6 @@
            if exts is None:
                exts = _RequestExtensions()
                self.registry.registerUtility(exts, IRequestExtensions)
                self.registry.registerHandler(_set_request_extensions,
                                              (INewRequest,))
            plist = exts.descriptors if property else exts.methods
            plist[name] = callable
@@ -201,10 +199,3 @@
        self.descriptors = {}
        self.methods = {}
def _set_request_extensions(event):
    request = event.request
    exts = request.registry.queryUtility(IRequestExtensions)
    for name, fn in iteritems_(exts.methods):
        method = fn.__get__(request, request.__class__)
        setattr(request, name, method)
    request._set_properties(exts.descriptors)
pyramid/router.py
@@ -6,6 +6,7 @@
from pyramid.interfaces import (
    IDebugLogger,
    IRequest,
    IRequestExtensions,
    IRootFactory,
    IRouteRequest,
    IRouter,
@@ -48,6 +49,7 @@
        self.root_factory = q(IRootFactory, default=DefaultRootFactory)
        self.routes_mapper = q(IRoutesMapper)
        self.request_factory = q(IRequestFactory, default=Request)
        self.request_extensions = q(IRequestExtensions)
        tweens = q(ITweens)
        if tweens is None:
            tweens = excview_tween_factory
@@ -178,6 +180,9 @@
        try:
            try:
                extensions = self.request_extensions
                if extensions is not None:
                    request._set_extensions(extensions)
                response = self.handle_request(request)
                has_listeners and notify(NewResponse(request, response))
@@ -192,4 +197,3 @@
        finally:
            manager.pop()
pyramid/scripting.py
@@ -3,6 +3,7 @@
from pyramid.request import Request
from pyramid.interfaces import (
    IRequestExtensions,
    IRequestFactory,
    IRootFactory,
    )
@@ -70,9 +71,11 @@
                                 'before trying to activate it.')
    if request is None:
        request = _make_request('/', registry)
    request.registry = registry
    threadlocals = {'registry':registry, 'request':request}
    threadlocal_manager.push(threadlocals)
    extensions = registry.queryUtility(IRequestExtensions)
    if extensions is not None:
        request._set_extensions(extensions)
    def closer():
        threadlocal_manager.pop()
    root_factory = registry.queryUtility(IRootFactory,
pyramid/tests/test_config/test_factories.py
@@ -112,36 +112,6 @@
        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)
        exts = list(sorted(event.request.extensions))
        self.assertEqual('bar', exts[0])
        self.assertEqual('foo', exts[1])
    def test_add_request_method_subscriber(self):
        from zope.interface import implementer
        from pyramid.interfaces import INewRequest
        config = self._makeOne(autocommit=True)
        def foo(r): return 'bar'
        config.add_request_method(foo, name='foo')
        @implementer(INewRequest)
        class Event(object):
            request = DummyRequest(config.registry)
        event = Event()
        config.registry.notify(event)
        self.assertEqual('bar', event.request.foo())
    def test_add_request_method_with_callable(self):
        from pyramid.interfaces import IRequestExtensions
        config = self._makeOne(autocommit=True)
pyramid/tests/test_router.py
@@ -312,6 +312,38 @@
        self.assertEqual(app_iter, [b'abc'])
        self.assertEqual(start_response.status, '200 OK')
    def test_call_with_request_extensions(self):
        from pyramid.interfaces import IViewClassifier
        from pyramid.interfaces import IRequestExtensions
        from pyramid.interfaces import IRequest
        from pyramid.request import Request
        context = DummyContext()
        self._registerTraverserFactory(context)
        class Extensions(object):
            def __init__(self):
                self.methods = {}
                self.descriptors = {}
        extensions = Extensions()
        L = []
        request = Request.blank('/')
        request.request_iface = IRequest
        request.registry = self.registry
        request._set_extensions = lambda *x: L.extend(x)
        def request_factory(environ):
            return request
        self.registry.registerUtility(extensions, IRequestExtensions)
        environ = self._makeEnviron()
        response = DummyResponse()
        response.app_iter = ['Hello world']
        view = DummyView(response)
        self._registerView(self.config.derive_view(view), '',
                           IViewClassifier, None, None)
        router = self._makeOne()
        router.request_factory = request_factory
        start_response = DummyStartResponse()
        router(environ, start_response)
        self.assertEqual(L, [extensions])
    def test_call_view_registered_nonspecific_default_path(self):
        from pyramid.interfaces import IViewClassifier
        context = DummyContext()
pyramid/tests/test_scripting.py
@@ -5,31 +5,37 @@
        from pyramid.scripting import get_root
        return get_root(app, request)
    def _makeRegistry(self):
        return DummyRegistry([DummyFactory])
    def test_it_norequest(self):
        app = DummyApp(registry=dummy_registry)
        registry = self._makeRegistry()
        app = DummyApp(registry=registry)
        root, closer = self._callFUT(app)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['registry'], registry)
        self.assertEqual(pushed['request'].registry, app.registry)
        self.assertEqual(len(app.threadlocal_manager.popped), 0)
        closer()
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
    def test_it_withrequest(self):
        app = DummyApp(registry=dummy_registry)
        registry = self._makeRegistry()
        app = DummyApp(registry=registry)
        request = DummyRequest({})
        root, closer = self._callFUT(app, request)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
        self.assertEqual(pushed['registry'], dummy_registry)
        self.assertEqual(pushed['registry'], registry)
        self.assertEqual(pushed['request'], request)
        self.assertEqual(len(app.threadlocal_manager.popped), 0)
        closer()
        self.assertEqual(len(app.threadlocal_manager.popped), 1)
    def test_it_requestfactory_overridden(self):
        app = DummyApp(registry=dummy_registry)
        registry = self._makeRegistry()
        app = DummyApp(registry=registry)
        root, closer = self._callFUT(app)
        self.assertEqual(len(app.threadlocal_manager.pushed), 1)
        pushed = app.threadlocal_manager.pushed[0]
@@ -40,8 +46,10 @@
        from pyramid.scripting import prepare
        return prepare(request, registry)
    def _makeRegistry(self):
        return DummyRegistry(DummyFactory)
    def _makeRegistry(self, L=None):
        if L is None:
            L = [None, DummyFactory]
        return DummyRegistry(L)
    def setUp(self):
        from pyramid.threadlocal import manager
@@ -53,7 +61,7 @@
        self.assertRaises(ConfigurationError, self._callFUT)
    def test_it_norequest(self):
        registry = self._makeRegistry()
        registry = self._makeRegistry([DummyFactory, None, DummyFactory])
        info = self._callFUT(registry=registry)
        root, closer = info['root'], info['closer']
        pushed = self.manager.get()
@@ -89,20 +97,32 @@
        closer()
        self.assertEqual(self.default, self.manager.get())
    def test_it_with_extensions(self):
        exts = Dummy()
        request = DummyRequest({})
        registry = request.registry = self._makeRegistry([exts, DummyFactory])
        info = self._callFUT(request=request, registry=registry)
        self.assertEqual(request.extensions, exts)
        root, closer = info['root'], info['closer']
        closer()
class Test__make_request(unittest.TestCase):
    def _callFUT(self, path='/', registry=None):
        from pyramid.scripting import _make_request
        return _make_request(path, registry)
    def _makeRegistry(self):
        return DummyRegistry([DummyFactory])
    def test_it_with_registry(self):
        request = self._callFUT('/', dummy_registry)
        registry = self._makeRegistry()
        request = self._callFUT('/', registry)
        self.assertEqual(request.environ['path'], '/')
        self.assertEqual(request.registry, dummy_registry)
        self.assertEqual(request.registry, registry)
    def test_it_with_no_registry(self):
        from pyramid.config import global_registries
        # keep registry local so that global_registries is cleared after
        registry = DummyRegistry(DummyFactory)
        registry = self._makeRegistry()
        global_registries.add(registry)
        try:
            request = self._callFUT('/hello')
@@ -127,13 +147,13 @@
        self.kw = kw
class DummyRegistry(object):
    def __init__(self, factory=None):
        self.factory = factory
    def __init__(self, utilities):
        self.utilities = utilities
    def queryUtility(self, iface, default=None):
        return self.factory or default
dummy_registry = DummyRegistry(DummyFactory)
        if self.utilities:
            return self.utilities.pop(0)
        return default
class DummyApp:
    def __init__(self, registry=None):
@@ -156,6 +176,10 @@
        self.popped.append(True)
        
class DummyRequest:
    matchdict = None
    matched_route = None
    def __init__(self, environ):
        self.environ = environ
    def _set_extensions(self, exts):
        self.extensions = exts
pyramid/tests/test_util.py
@@ -132,6 +132,20 @@
        self.assertEqual(1, foo.x)
        self.assertEqual(2, foo.y)
    def test__set_extensions(self):
        inst = self._makeOne()
        def foo(self, result):
            return result
        n, bar = inst._make_property(lambda _: 'bar', name='bar')
        class Extensions(object):
            def __init__(self):
                self.methods = {'foo':foo}
                self.descriptors = {'bar':bar}
        extensions = Extensions()
        inst._set_extensions(extensions)
        self.assertEqual(inst.bar, 'bar')
        self.assertEqual(inst.foo('abc'), 'abc')
class Test_WeakOrderedSet(unittest.TestCase):
    def _makeOne(self):
        from pyramid.config import WeakOrderedSet
pyramid/util.py
@@ -2,6 +2,7 @@
import weakref
from pyramid.compat import (
    iteritems_,
    integer_types,
    string_types,
    text_,
@@ -74,6 +75,12 @@
        cls = type(parent.__name__, (parent, object), attrs)
        self.__class__ = cls
    def _set_extensions(self, extensions):
        for name, fn in iteritems_(extensions.methods):
            method = fn.__get__(self, self.__class__)
            setattr(self, name, method)
        self._set_properties(extensions.descriptors)
    def set_property(self, callable, name=None, reify=False):
        """ Add a callable or a property descriptor to the instance.