From a196ba96fea9ee0b8db484a9562446d8ba64d9f2 Mon Sep 17 00:00:00 2001 From: Michael Merickel <michael@merickel.org> Date: Sat, 08 Jul 2017 19:44:51 +0200 Subject: [PATCH] Merge pull request #3123 from mmerickel/backport-3122-to-1.9-branch --- pyramid/response.py | 30 +++++++++- pyramid/tests/test_response.py | 21 +++++- pyramid/tests/test_events.py | 15 ++++- pyramid/events.py | 25 ++++++++ CHANGES.txt | 14 +++- pyramid/view.py | 29 +++++++-- pyramid/tests/test_view.py | 45 +++++++++++++- 7 files changed, 153 insertions(+), 26 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bca34a5..a5e1035 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,10 +1,16 @@ unreleased ========== -- Add a ``_category`` argument to the ``pyramid.view.view_config`` decorator. - This argument will be forwarded to venusian and can be used to affect - what items are picked up by ``config.scan(..., category=...)``. - See https://github.com/Pylons/pyramid/pull/3121 +- Add a ``_depth`` and ``_category`` arguments to all of the venusian + decorators. The ``_category`` argument can be used to affect which actions + are registered when performing a ``config.scan(..., category=...)`` with a + specific category. The ``_depth`` argument should be used when wrapping + the decorator in your own. This change affects ``pyramid.view.view_config``, + ``pyramid.view.exception_view_config``, + ``pyramid.view.forbidden_view_config``, ``pyramid.view.notfound_view_config``, + ``pyramid.events.subscriber`` and ``pyramid.response.response_adapter`` + decorators. See https://github.com/Pylons/pyramid/pull/3121 and + https://github.com/Pylons/pyramid/pull/3123 1.9 (2017-06-26) ================ diff --git a/pyramid/events.py b/pyramid/events.py index 35da2fa..d3b068b 100644 --- a/pyramid/events.py +++ b/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) diff --git a/pyramid/response.py b/pyramid/response.py index 1d9daae..1e2546e 100644 --- a/pyramid/response.py +++ b/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 diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py index 52e53c3..4f9011c 100644 --- a/pyramid/tests/test_events.py +++ b/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 diff --git a/pyramid/tests/test_response.py b/pyramid/tests/test_response.py index ad55882..53e3ce1 100644 --- a/pyramid/tests/test_response.py +++ b/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)) diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py index 7a1c90d..0124ce6 100644 --- a/pyramid/tests/test_view.py +++ b/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 diff --git a/pyramid/view.py b/pyramid/view.py index 3b2bafa..46aec45 100644 --- a/pyramid/view.py +++ b/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 -- Gitblit v1.9.3