diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-10-18 02:27:43 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-10-18 02:27:43 +0000 |
| commit | 2cce431f02a37c119eacfc3dfa94af9fe3305de1 (patch) | |
| tree | f154dbf4937afb3f5d08ed497f6c4cac6f843144 | |
| parent | f276669b505cb0565dcf854f2e0e751151ccf836 (diff) | |
| download | pyramid-2cce431f02a37c119eacfc3dfa94af9fe3305de1.tar.gz pyramid-2cce431f02a37c119eacfc3dfa94af9fe3305de1.tar.bz2 pyramid-2cce431f02a37c119eacfc3dfa94af9fe3305de1.zip | |
- More than one ``@bfg_view`` decorator may now be stacked on top of
any number of others. Each invocation of the decorator registers a
single view. For instance, the following combination of decorators
and a function will register two views::
from repoze.bfg.view import bfg_view
@bfg_view(name='edit')
@bfg_view(name='change')
def edit(context, request):
pass
This makes it possible to associate more than one view configuration
for a single callable without requiring ZCML.
| -rw-r--r-- | CHANGES.txt | 21 | ||||
| -rw-r--r-- | docs/narr/views.rst | 21 | ||||
| -rw-r--r-- | repoze/bfg/tests/grokkedapp/__init__.py | 16 | ||||
| -rw-r--r-- | repoze/bfg/tests/grokkedapp/another.py | 18 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_integration.py | 119 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_view.py | 21 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 2 | ||||
| -rw-r--r-- | repoze/bfg/view.py | 4 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 7 |
9 files changed, 159 insertions, 70 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 2b3181d9d..b9de23d9f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,24 @@ +Next release +============ + +Features +-------- + +- More than one ``@bfg_view`` decorator may now be stacked on top of + any number of others. Each invocation of the decorator registers a + single view. For instance, the following combination of decorators + and a function will register two views:: + + from repoze.bfg.view import bfg_view + + @bfg_view(name='edit') + @bfg_view(name='change') + def edit(context, request): + pass + + This makes it possible to associate more than one view configuration + for a single callable without requiring ZCML. + 1.1a6 (2009-10-15) ================== diff --git a/docs/narr/views.rst b/docs/narr/views.rst index 3979ec8f6..b52abf57f 100644 --- a/docs/narr/views.rst +++ b/docs/narr/views.rst @@ -708,6 +708,27 @@ decorator syntactic sugar), if you wish: my_view = bfg_view()(MyView) +More than one ``bfg_view`` decorator can be stacked on top of any +number of others. Each decorator creates a separate view +registration. For example: + +.. code-block:: python + :linenos: + + from repoze.bfg.view import bfg_view + + @bfg_view(name='edit') + @bfg_view(name='change') + def edit(context, request): + pass + +This registers the same view under two different names. + +.. note:: ``bfg_view`` decorator stacking is a feature new in + :mod:`repoze.bfg` 1.1. Previously, these decorators could not be + stacked without the effect of the "upper" decorator cancelling the + effect of the the decorator beneath it. + .. _view_lookup_ordering: View Lookup Ordering diff --git a/repoze/bfg/tests/grokkedapp/__init__.py b/repoze/bfg/tests/grokkedapp/__init__.py index f4a924292..5a00adbeb 100644 --- a/repoze/bfg/tests/grokkedapp/__init__.py +++ b/repoze/bfg/tests/grokkedapp/__init__.py @@ -7,6 +7,22 @@ def grokked(context, request): @bfg_view(request_method='POST') def grokked_post(context, request): return 'grokked_post' + +@bfg_view(name='stacked2') +@bfg_view(name='stacked1') +def stacked(context, request): + return 'stacked' + +class stacked_class(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'stacked_class' + +stacked_class = bfg_view(name='stacked_class1')(stacked_class) +stacked_class = bfg_view(name='stacked_class2')(stacked_class) class oldstyle_grokked_class: def __init__(self, context, request): diff --git a/repoze/bfg/tests/grokkedapp/another.py b/repoze/bfg/tests/grokkedapp/another.py index 94be9f74a..7dda1d579 100644 --- a/repoze/bfg/tests/grokkedapp/another.py +++ b/repoze/bfg/tests/grokkedapp/another.py @@ -7,7 +7,23 @@ def grokked(context, request): @bfg_view(request_method='POST', name='another') def grokked_post(context, request): return 'another_grokked_post' - + +@bfg_view(name='another_stacked2') +@bfg_view(name='another_stacked1') +def stacked(context, request): + return 'another_stacked' + +class stacked_class(object): + def __init__(self, context, request): + self.context = context + self.request = request + + def __call__(self): + return 'another_stacked_class' + +stacked_class = bfg_view(name='another_stacked_class1')(stacked_class) +stacked_class = bfg_view(name='another_stacked_class2')(stacked_class) + class oldstyle_grokked_class: def __init__(self, context, request): self.context = context diff --git a/repoze/bfg/tests/test_integration.py b/repoze/bfg/tests/test_integration.py index 5e386a069..691d64e24 100644 --- a/repoze/bfg/tests/test_integration.py +++ b/repoze/bfg/tests/test_integration.py @@ -105,68 +105,43 @@ class TestGrokkedApp(unittest.TestCase): from repoze.bfg.interfaces import IView from repoze.bfg.interfaces import IRequest import repoze.bfg.tests.grokkedapp as package - actions = zcml_configure('configure.zcml', package)[-10:] + + actions = zcml_configure('configure.zcml', package) actions.sort() - action = actions[0] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], '') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[1] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], '') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[2] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'another') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[3] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'another') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[4] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'another_grokked_class') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[5] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'another_grokked_instance') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[6] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'another_oldstyle_grokked_class') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[7] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'grokked_class') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[8] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'grokked_instance') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) - - action = actions[9] - self.assertEqual(action[0][1], None) - self.assertEqual(action[0][2], 'oldstyle_grokked_class') - self.assertEqual(action[0][3], IRequest) - self.assertEqual(action[0][4], IView) + num = 18 + + action_names = [actions[x][0][2] for x in range(len(actions[:num]))] + action_types = [(actions[x][0][1], + actions[x][0][3], + actions[x][0][4]) for x in range(len(actions[:num]))] + + for typ in action_types: + self.assertEqual(typ, (None, IRequest, IView)) + + self.assertEqual( + action_names, [ + '', + '', + 'another', + 'another', + 'another_grokked_class', + 'another_grokked_instance', + 'another_oldstyle_grokked_class', + 'another_stacked1', + 'another_stacked2', + 'another_stacked_class1', + 'another_stacked_class2', + 'grokked_class', + 'grokked_instance', + 'oldstyle_grokked_class', + 'stacked1', + 'stacked2', + 'stacked_class1', + 'stacked_class2', + ] + ) + ctx = DummyContext() req = DummyRequest() @@ -207,6 +182,30 @@ class TestGrokkedApp(unittest.TestCase): 'another_oldstyle_grokked_class') self.assertEqual(result, 'another_oldstyle_grokked_class') + result = render_view_to_response(ctx, req, 'stacked1') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'stacked2') + self.assertEqual(result, 'stacked') + + result = render_view_to_response(ctx, req, 'another_stacked1') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'another_stacked2') + self.assertEqual(result, 'another_stacked') + + result = render_view_to_response(ctx, req, 'stacked_class1') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'stacked_class2') + self.assertEqual(result, 'stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class1') + self.assertEqual(result, 'another_stacked_class') + + result = render_view_to_response(ctx, req, 'another_stacked_class2') + self.assertEqual(result, 'another_stacked_class') + class DummyContext(object): pass diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py index eee5a61bd..7b9597194 100644 --- a/repoze/bfg/tests/test_view.py +++ b/repoze/bfg/tests/test_view.py @@ -376,7 +376,7 @@ class TestBFGViewDecorator(unittest.TestCase): """ docstring """ wrapped = decorator(foo) self.failUnless(wrapped is foo) - settings = wrapped.__bfg_view_settings__ + settings = wrapped.__bfg_view_settings__[0] self.assertEqual(settings['permission'], None) self.assertEqual(settings['for_'], None) self.assertEqual(settings['request_type'], None) @@ -387,7 +387,7 @@ class TestBFGViewDecorator(unittest.TestCase): """ docstring """ wrapped = decorator(foo) self.failUnless(wrapped is foo) - settings = wrapped.__bfg_view_settings__ + settings = wrapped.__bfg_view_settings__[0] self.assertEqual(settings['permission'], None) self.assertEqual(settings['for_'], None) self.assertEqual(settings['request_type'], None) @@ -398,11 +398,26 @@ class TestBFGViewDecorator(unittest.TestCase): """ docstring """ wrapped = decorator(foo) self.failUnless(wrapped is foo) - settings = wrapped.__bfg_view_settings__ + settings = wrapped.__bfg_view_settings__[0] self.assertEqual(settings['permission'], None) self.assertEqual(settings['for_'], None) self.assertEqual(settings['request_type'], None) + def test_stacking(self): + decorator1 = self._makeOne(name='1') + decorator2 = self._makeOne(name='2') + def foo(): + """ docstring """ + wrapped1 = decorator1(foo) + wrapped2 = decorator2(wrapped1) + self.failUnless(wrapped1 is foo) + self.failUnless(wrapped2 is foo) + self.assertEqual(len(wrapped2.__bfg_view_settings__), 2) + settings1 = wrapped2.__bfg_view_settings__[0] + self.assertEqual(settings1['name'], '1') + settings2 = wrapped2.__bfg_view_settings__[1] + self.assertEqual(settings2['name'], '2') + class TestDefaultForbiddenView(unittest.TestCase): def _callFUT(self, context, request): from repoze.bfg.view import default_forbidden_view diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py index 77137ceba..7538072ca 100644 --- a/repoze/bfg/tests/test_zcml.py +++ b/repoze/bfg/tests/test_zcml.py @@ -2390,7 +2390,7 @@ class TestBFGViewGrokker(unittest.TestCase): containment=None, attr=None, renderer=None, wrapper_viewname=None, xhr=False, header=None, accept=None) - obj.__bfg_view_settings__ = settings + obj.__bfg_view_settings__ = [settings] context = DummyContext() result = grokker.grok('name', obj, context=context) self.assertEqual(result, True) diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py index d6c47ec0f..7f71f2c57 100644 --- a/repoze/bfg/view.py +++ b/repoze/bfg/view.py @@ -394,7 +394,9 @@ class bfg_view(object): self.header = header def __call__(self, wrapped): - wrapped.__bfg_view_settings__ = self.__dict__.copy() + settings = getattr(wrapped, '__bfg_view_settings__', []) + settings.append(self.__dict__.copy()) + wrapped.__bfg_view_settings__ = settings return wrapped def default_view(context, request, status): diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py index a4c18dda9..681c4da1e 100644 --- a/repoze/bfg/zcml.py +++ b/repoze/bfg/zcml.py @@ -693,8 +693,8 @@ class BFGMultiGrokker(martian.core.MultiInstanceOrClassGrokkerBase): class BFGViewGrokker(martian.InstanceGrokker): martian.component(BFGViewMarker) def grok(self, name, obj, **kw): - if hasattr(obj, '__bfg_view_settings__'): - settings = obj.__bfg_view_settings__ + config = getattr(obj, '__bfg_view_settings__', []) + for settings in config: permission = settings['permission'] for_ = settings['for_'] name = settings['name'] @@ -716,8 +716,7 @@ class BFGViewGrokker(martian.InstanceGrokker): request_param=request_param, containment=containment, attr=attr, renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept, header=header) - return True - return False + return bool(config) def exclude(name): if name.startswith('.'): |
