Michael Merickel
2017-07-08 0109711792b0735488342b7e3efe4ee1fb303dbd
add _depth and _category arguments to all decorators

``subscriber``, ``response_adapter``, ``exception_view_config``,
``notfound_view_config``, ``forbidden_view_config``

This is an extension of #3105 to add support to the remaining decorators.
6 files modified
165 ■■■■ changed files
pyramid/events.py 25 ●●●●● patch | view | raw | blame | history
pyramid/response.py 30 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_events.py 15 ●●●● patch | view | raw | blame | history
pyramid/tests/test_response.py 21 ●●●● patch | view | raw | blame | history
pyramid/tests/test_view.py 45 ●●●●● patch | view | raw | blame | history
pyramid/view.py 29 ●●●● patch | view | raw | blame | history
pyramid/events.py
@@ -68,12 +68,34 @@
    :ref:`subscriber_predicates` for a description of how predicates can
    narrow the set of circumstances in which a subscriber will be called.
    Two additional keyword arguments which will be passed to the
    :term:`venusian` ``attach`` function are ``_depth`` and ``_category``.
    ``_depth`` is provided for people who wish to reuse this class from another
    decorator. The default value is ``0`` and should be specified relative to
    the ``subscriber`` invocation. It will be passed in to the
    :term:`venusian` ``attach`` function as the depth of the callstack when
    Venusian checks if the decorator is being used in a class or module
    context. It's not often used, but it can be useful in this circumstance.
    ``_category`` sets the decorator category name. It can be useful in
    combination with the ``category`` argument of ``scan`` to control which
    views should be processed.
    See the :py:func:`venusian.attach` function in Venusian for more
    information about the ``_depth`` and ``_category`` arguments.
    .. versionchanged:: 1.9.1
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian # for unit testing
    def __init__(self, *ifaces, **predicates):
        self.ifaces = ifaces
        self.predicates = predicates
        self.depth = predicates.pop('_depth', 0)
        self.category = predicates.pop('_category', 'pyramid')
    def register(self, scanner, name, wrapped):
        config = scanner.config
@@ -81,7 +103,8 @@
            config.add_subscriber(wrapped, iface, **self.predicates)
    def __call__(self, wrapped):
        self.venusian.attach(wrapped, self.register, category='pyramid')
        self.venusian.attach(wrapped, self.register, category=self.category,
                             depth=self.depth + 1)
        return wrapped
@implementer(INewRequest)
pyramid/response.py
@@ -145,19 +145,43 @@
        config = Configurator()
        config.scan('somepackage_containing_adapters')
    Two additional keyword arguments which will be passed to the
    :term:`venusian` ``attach`` function are ``_depth`` and ``_category``.
    ``_depth`` is provided for people who wish to reuse this class from another
    decorator. The default value is ``0`` and should be specified relative to
    the ``response_adapter`` invocation. It will be passed in to the
    :term:`venusian` ``attach`` function as the depth of the callstack when
    Venusian checks if the decorator is being used in a class or module
    context. It's not often used, but it can be useful in this circumstance.
    ``_category`` sets the decorator category name. It can be useful in
    combination with the ``category`` argument of ``scan`` to control which
    views should be processed.
    See the :py:func:`venusian.attach` function in Venusian for more
    information about the ``_depth`` and ``_category`` arguments.
    .. versionchanged:: 1.9.1
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian # for unit testing
    def __init__(self, *types_or_ifaces):
    def __init__(self, *types_or_ifaces, **kwargs):
        self.types_or_ifaces = types_or_ifaces
        self.depth = kwargs.pop('_depth', 0)
        self.category = kwargs.pop('_category', 'pyramid')
        self.kwargs = kwargs
    def register(self, scanner, name, wrapped):
        config = scanner.config
        for type_or_iface in self.types_or_ifaces:
            config.add_response_adapter(wrapped, type_or_iface)
            config.add_response_adapter(wrapped, type_or_iface, **self.kwargs)
    def __call__(self, wrapped):
        self.venusian.attach(wrapped, self.register, category='pyramid')
        self.venusian.attach(wrapped, self.register, category=self.category,
                             depth=self.depth + 1)
        return wrapped
pyramid/tests/test_events.py
@@ -209,7 +209,16 @@
        def foo(): pass
        dec(foo)
        self.assertEqual(dummy_venusian.attached,
                         [(foo, dec.register, 'pyramid')])
                         [(foo, dec.register, 'pyramid', 1)])
    def test___call___with_venusian_args(self):
        dec = self._makeOne(_category='foo', _depth=1)
        dummy_venusian = DummyVenusian()
        dec.venusian = dummy_venusian
        def foo(): pass
        dec(foo)
        self.assertEqual(dummy_venusian.attached,
                         [(foo, dec.register, 'foo', 2)])
    def test_regsister_with_predicates(self):
        from zope.interface import Interface
@@ -308,8 +317,8 @@
    def __init__(self):
        self.attached = []
    def attach(self, wrapped, fn, category=None):
        self.attached.append((wrapped, fn, category))
    def attach(self, wrapped, fn, category=None, depth=None):
        self.attached.append((wrapped, fn, category, depth))
class Dummy:
    pass
pyramid/tests/test_response.py
@@ -136,9 +136,9 @@
    def tearDown(self):
        self.config.end()
    def _makeOne(self, *types_or_ifaces):
    def _makeOne(self, *types_or_ifaces, **kw):
        from pyramid.response import response_adapter
        return response_adapter(*types_or_ifaces)
        return response_adapter(*types_or_ifaces, **kw)
    def test_register_single(self):
        from zope.interface import Interface
@@ -172,7 +172,18 @@
        def foo(): pass
        dec(foo)
        self.assertEqual(dummy_venusian.attached,
                         [(foo, dec.register, 'pyramid')])
                         [(foo, dec.register, 'pyramid', 1)])
    def test___call___with_venusian_args(self):
        from zope.interface import Interface
        class IFoo(Interface): pass
        dec = self._makeOne(IFoo, _category='foo', _depth=1)
        dummy_venusian = DummyVenusian()
        dec.venusian = dummy_venusian
        def foo(): pass
        dec(foo)
        self.assertEqual(dummy_venusian.attached,
                         [(foo, dec.register, 'foo', 2)])
class TestGetResponseFactory(unittest.TestCase):
@@ -199,5 +210,5 @@
    def __init__(self):
        self.attached = []
    def attach(self, wrapped, fn, category=None):
        self.attached.append((wrapped, fn, category))
    def attach(self, wrapped, fn, category=None, depth=None):
        self.attached.append((wrapped, fn, category, depth))
pyramid/tests/test_view.py
@@ -91,6 +91,18 @@
        self.assertEqual(settings[0]['attr'], 'view')
        self.assertEqual(settings[0]['_info'], 'codeinfo')
    def test_call_with_venusian_args(self):
        decorator = self._makeOne(_depth=1, _category='foo')
        venusian = DummyVenusian()
        decorator.venusian = venusian
        def foo(): pass
        decorator(foo)
        attachments = venusian.attachments
        category = attachments[0][2]
        depth = attachments[0][3]
        self.assertEqual(depth, 2)
        self.assertEqual(category, 'foo')
class Test_forbidden_view_config(BaseTest, unittest.TestCase):
    def _makeOne(self, **kw):
        from pyramid.view import forbidden_view_config
@@ -132,6 +144,18 @@
        self.assertEqual(settings[0]['view'], None) # comes from call_venusian
        self.assertEqual(settings[0]['attr'], 'view')
        self.assertEqual(settings[0]['_info'], 'codeinfo')
    def test_call_with_venusian_args(self):
        decorator = self._makeOne(_depth=1, _category='foo')
        venusian = DummyVenusian()
        decorator.venusian = venusian
        def foo(): pass
        decorator(foo)
        attachments = venusian.attachments
        category = attachments[0][2]
        depth = attachments[0][3]
        self.assertEqual(depth, 2)
        self.assertEqual(category, 'foo')
class Test_exception_view_config(BaseTest, unittest.TestCase):
    def _makeOne(self, *args, **kw):
@@ -183,6 +207,18 @@
        self.assertEqual(settings[0]['view'], None) # comes from call_venusian
        self.assertEqual(settings[0]['attr'], 'view')
        self.assertEqual(settings[0]['_info'], 'codeinfo')
    def test_call_with_venusian_args(self):
        decorator = self._makeOne(_depth=1, _category='foo')
        venusian = DummyVenusian()
        decorator.venusian = venusian
        def foo(): pass
        decorator(foo)
        attachments = venusian.attachments
        category = attachments[0][2]
        depth = attachments[0][3]
        self.assertEqual(depth, 2)
        self.assertEqual(category, 'foo')
class RenderViewToResponseTests(BaseTest, unittest.TestCase):
    def _callFUT(self, *arg, **kw):
@@ -564,7 +600,9 @@
        decorator.venusian = venusian
        def foo(): pass
        decorator(foo)
        self.assertEqual(venusian.depth, 2)
        attachments = venusian.attachments
        depth = attachments[0][3]
        self.assertEqual(depth, 2)
    def test_call_withoutcategory(self):
        decorator = self._makeOne()
@@ -1000,8 +1038,7 @@
        self.attachments = []
    def attach(self, wrapped, callback, category=None, depth=1):
        self.attachments.append((wrapped, callback, category))
        self.depth = depth
        self.attachments.append((wrapped, callback, category, depth))
        return self.info
class DummyRegistry(object):
@@ -1028,7 +1065,7 @@
def call_venusian(venusian, context=None):
    if context is None:
        context = DummyVenusianContext()
    for wrapped, callback, category in venusian.attachments:
    for wrapped, callback, category, depth in venusian.attachments:
        callback(context, None, None)
    return context.config
pyramid/view.py
@@ -199,7 +199,8 @@
    combination with the ``category`` argument of ``scan`` to control which
    views should be processed.
    See the :py:func:`venusian.attach` function in Venusian for more information.
    See the :py:func:`venusian.attach` function in Venusian for more
    information about the ``_depth`` and ``_category`` arguments.
    
    .. seealso::
    
@@ -395,9 +396,10 @@
    being used, :class:`~pyramid.httpexceptions.HTTPMovedPermanently will
    be used` for the redirect response if a slash-appended route is found.
    .. versionchanged:: 1.6
    See :ref:`changing_the_notfound_view` for detailed usage information.
    .. versionchanged:: 1.9.1
       Added the ``_depth`` and ``_category`` arguments.
    """
@@ -408,12 +410,15 @@
    def __call__(self, wrapped):
        settings = self.__dict__.copy()
        depth = settings.pop('_depth', 0)
        category = settings.pop('_category', 'pyramid')
        def callback(context, name, ob):
            config = context.config.with_package(info.module)
            config.add_notfound_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category='pyramid')
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
@@ -455,6 +460,9 @@
    See :ref:`changing_the_forbidden_view` for detailed usage information.
    .. versionchanged:: 1.9.1
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian
@@ -464,12 +472,15 @@
    def __call__(self, wrapped):
        settings = self.__dict__.copy()
        depth = settings.pop('_depth', 0)
        category = settings.pop('_category', 'pyramid')
        def callback(context, name, ob):
            config = context.config.with_package(info.module)
            config.add_forbidden_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category='pyramid')
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
@@ -511,6 +522,9 @@
    :meth:`pyramid.view.view_config`, and each predicate argument restricts
    the set of circumstances under which this exception view will be invoked.
    .. versionchanged:: 1.9.1
       Added the ``_depth`` and ``_category`` arguments.
    """
    venusian = venusian
@@ -524,12 +538,15 @@
    def __call__(self, wrapped):
        settings = self.__dict__.copy()
        depth = settings.pop('_depth', 0)
        category = settings.pop('_category', 'pyramid')
        def callback(context, name, ob):
            config = context.config.with_package(info.module)
            config.add_exception_view(view=ob, **settings)
        info = self.venusian.attach(wrapped, callback, category='pyramid')
        info = self.venusian.attach(wrapped, callback, category=category,
                                    depth=depth + 1)
        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or