Skip to content

Commit

Permalink
add _depth and _category arguments to all decorators
Browse files Browse the repository at this point in the history
``subscriber``, ``response_adapter``, ``exception_view_config``,
``notfound_view_config``, ``forbidden_view_config``

This is an extension of Pylons#3105 to add support to the remaining decorators.
  • Loading branch information
mmerickel committed Jul 8, 2017
1 parent a0aaf09 commit 4981584
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 22 deletions.
25 changes: 24 additions & 1 deletion pyramid/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,43 @@ def mysubscriber(event):
: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
for iface in self.ifaces or (Interface,):
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)
Expand Down
30 changes: 27 additions & 3 deletions pyramid/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,19 +145,43 @@ def myadapter(ob):
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


Expand Down
15 changes: 12 additions & 3 deletions pyramid/tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,16 @@ def test___call__(self):
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
Expand Down Expand Up @@ -308,8 +317,8 @@ class DummyVenusian(object):
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
Expand Down
21 changes: 16 additions & 5 deletions pyramid/tests/test_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ def setUp(self):
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
Expand Down Expand Up @@ -172,7 +172,18 @@ class IFoo(Interface): pass
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):
Expand All @@ -199,5 +210,5 @@ class DummyVenusian(object):
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))
45 changes: 41 additions & 4 deletions pyramid/tests/test_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ class view(object): pass
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
Expand Down Expand Up @@ -133,6 +145,18 @@ class view(object): pass
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):
from pyramid.view import exception_view_config
Expand Down Expand Up @@ -184,6 +208,18 @@ class view(object): pass
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):
from pyramid.view import render_view_to_response
Expand Down Expand Up @@ -564,7 +600,9 @@ def test_call_withdepth(self):
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()
Expand Down Expand Up @@ -1000,8 +1038,7 @@ def __init__(self, info=None):
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):
Expand All @@ -1028,7 +1065,7 @@ def __init__(self):
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

29 changes: 23 additions & 6 deletions pyramid/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ def my_view(context, request):
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::
Expand Down Expand Up @@ -395,10 +396,11 @@ def aview(request):
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.
"""

venusian = venusian
Expand All @@ -408,12 +410,15 @@ def __init__(self, **settings):

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
Expand Down Expand Up @@ -455,6 +460,9 @@ def forbidden(request):
See :ref:`changing_the_forbidden_view` for detailed usage information.
.. versionchanged:: 1.9.1
Added the ``_depth`` and ``_category`` arguments.
"""

venusian = venusian
Expand All @@ -464,12 +472,15 @@ def __init__(self, **settings):

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
Expand Down Expand Up @@ -511,6 +522,9 @@ def error_view(request):
: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

Expand All @@ -524,12 +538,15 @@ def __init__(self, *args, **settings):

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
Expand Down

0 comments on commit 4981584

Please sign in to comment.