diff options
| author | Chris McDonough <chrism@plope.com> | 2012-01-06 02:16:03 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2012-01-06 02:16:14 -0500 |
| commit | 4c29ef5be6e7bb1418ce896a92f4c8bd6d9a3111 (patch) | |
| tree | 10dc0af568381e30c4ab5eb6f6f1611c40bbc157 | |
| parent | 683941663033bbac588b9ac8f1ff28eeefc511c9 (diff) | |
| download | pyramid-4c29ef5be6e7bb1418ce896a92f4c8bd6d9a3111.tar.gz pyramid-4c29ef5be6e7bb1418ce896a92f4c8bd6d9a3111.tar.bz2 pyramid-4c29ef5be6e7bb1418ce896a92f4c8bd6d9a3111.zip | |
- The ``pyramid.view.view_defaults`` decorator did not work properly when
more than one view relied on the defaults being different for configuration
conflict resolution. See https://github.com/Pylons/pyramid/issues/394.
Closes #394.
| -rw-r--r-- | CHANGES.txt | 10 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_views.py | 38 | ||||
| -rw-r--r-- | pyramid/tests/test_view.py | 40 | ||||
| -rw-r--r-- | pyramid/view.py | 55 |
4 files changed, 95 insertions, 48 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 339317beb..d5ef9dd29 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,13 @@ +Next release +============ + +Bug Fixes +--------- + +- The ``pyramid.view.view_defaults`` decorator did not work properly when + more than one view relied on the defaults being different for configuration + conflict resolution. See https://github.com/Pylons/pyramid/issues/394. + 1.3a4 (2012-01-05) ================== diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index bcf1faa08..6563fe772 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -1433,11 +1433,47 @@ class TestViewsConfigurationMixin(unittest.TestCase): directlyProvides(context, IDummy) request = self._makeRequest(config) self.assertEqual(wrapper(context, request), 'OK') - context = DummyContext() request = self._makeRequest(config) self.assertRaises(PredicateMismatch, wrapper, context, request) + def test_add_view_with_view_config_and_view_defaults_doesnt_conflict(self): + from pyramid.renderers import null_renderer + class view(object): + __view_defaults__ = { + 'containment':'pyramid.tests.test_config.IDummy' + } + class view2(object): + __view_defaults__ = { + 'containment':'pyramid.tests.test_config.IFactory' + } + config = self._makeOne(autocommit=False) + config.add_view( + view=view, + renderer=null_renderer) + config.add_view( + view=view2, + renderer=null_renderer) + config.commit() # does not raise + + def test_add_view_with_view_config_and_view_defaults_conflicts(self): + from pyramid.renderers import null_renderer + class view(object): + __view_defaults__ = { + 'containment':'pyramid.tests.test_config.IDummy' + } + class view2(object): + __view_defaults__ = { + 'containment':'pyramid.tests.test_config.IDummy' + } + config = self._makeOne(autocommit=False) + config.add_view( + view=view, + renderer=null_renderer) + config.add_view( + view=view2, + renderer=null_renderer) + self.assertRaises(ConfigurationConflictError, config.commit) def test_derive_view_function(self): from pyramid.renderers import null_renderer diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py index 0d00e65c6..03a111828 100644 --- a/pyramid/tests/test_view.py +++ b/pyramid/tests/test_view.py @@ -260,10 +260,7 @@ class TestViewConfigDecorator(unittest.TestCase): def test_create_defaults(self): decorator = self._makeOne() - self.assertEqual(decorator.name, '') - self.assertEqual(decorator.request_type, None) - self.assertEqual(decorator.context, None) - self.assertEqual(decorator.permission, None) + self.assertEqual(decorator.__dict__, {}) def test_create_context_trumps_for(self): decorator = self._makeOne(context='123', for_='456') @@ -274,9 +271,11 @@ class TestViewConfigDecorator(unittest.TestCase): self.assertEqual(decorator.context, '456') def test_create_nondefaults(self): - decorator = self._makeOne(name=None, request_type=None, for_=None, - permission='foo', mapper='mapper', - decorator='decorator', match_param='match_param') + decorator = self._makeOne( + name=None, request_type=None, for_=None, + permission='foo', mapper='mapper', + decorator='decorator', match_param='match_param' + ) self.assertEqual(decorator.name, None) self.assertEqual(decorator.request_type, None) self.assertEqual(decorator.context, None) @@ -295,9 +294,11 @@ class TestViewConfigDecorator(unittest.TestCase): config = call_venusian(venusian) settings = config.settings self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['permission'], None) - self.assertEqual(settings[0]['context'], None) - self.assertEqual(settings[0]['request_type'], None) + self.assertEqual(len(settings), 1) + self.assertEqual(len(settings[0]), 3) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian + self.assertEqual(settings[0]['_info'], 'codeinfo') def test_call_class(self): decorator = self._makeOne() @@ -310,10 +311,11 @@ class TestViewConfigDecorator(unittest.TestCase): config = call_venusian(venusian) settings = config.settings self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['permission'], None) - self.assertEqual(settings[0]['context'], None) - self.assertEqual(settings[0]['request_type'], None) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian self.assertEqual(settings[0]['attr'], 'foo') + self.assertEqual(settings[0]['_info'], 'codeinfo') def test_call_class_attr_already_set(self): decorator = self._makeOne(attr='abc') @@ -326,10 +328,11 @@ class TestViewConfigDecorator(unittest.TestCase): config = call_venusian(venusian) settings = config.settings self.assertEqual(len(settings), 1) - self.assertEqual(settings[0]['permission'], None) - self.assertEqual(settings[0]['context'], None) - self.assertEqual(settings[0]['request_type'], None) + self.assertEqual(len(settings[0]), 4) + self.assertEqual(settings[0]['venusian'], venusian) + self.assertEqual(settings[0]['view'], None) # comes from call_venusian self.assertEqual(settings[0]['attr'], 'abc') + self.assertEqual(settings[0]['_info'], 'codeinfo') def test_stacking(self): decorator1 = self._makeOne(name='1') @@ -593,7 +596,7 @@ class Test_view_defaults(unittest.TestCase): @view_defaults(route_name='ghi') class Bar(Foo): pass self.assertEqual(Bar.__view_defaults__['route_name'],'ghi') - self.assertEqual(Bar.__view_defaults__['renderer'], None) + self.assertFalse('renderer' in Bar.__view_defaults__) def test_it_inheritance_overriden_empty(self): from pyramid.view import view_defaults @@ -601,8 +604,7 @@ class Test_view_defaults(unittest.TestCase): class Foo(object): pass @view_defaults() class Bar(Foo): pass - self.assertEqual(Bar.__view_defaults__['route_name'], None) - self.assertEqual(Bar.__view_defaults__['renderer'], None) + self.assertEqual(Bar.__view_defaults__, {}) class ExceptionResponse(Exception): status = '404 Not Found' diff --git a/pyramid/view.py b/pyramid/view.py index eae56a661..a68f9ad8a 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -133,6 +133,15 @@ def render_view(context, request, name='', secure=True): return None return ''.join(iterable) +class _default(object): + def __nonzero__(self): + return False + __bool__ = __nonzero__ + def __repr__(self): # pragma: no cover + return '(default)' + +default = _default() + class view_config(object): """ A function, class or method :term:`decorator` which allows a developer to create view registrations nearer to a :term:`view @@ -168,39 +177,29 @@ class view_config(object): and ``match_param``. The meanings of these arguments are the same as the arguments passed to - :meth:`pyramid.config.Configurator.add_view`. + :meth:`pyramid.config.Configurator.add_view`. If any argument is left + out, its default will be the equivalent ``add_view`` default. See :ref:`mapping_views_using_a_decorator_section` for details about using :class:`view_config`. """ venusian = venusian # for testing injection - def __init__(self, name='', request_type=None, for_=None, permission=None, - route_name=None, request_method=None, request_param=None, - containment=None, attr=None, renderer=None, wrapper=None, - xhr=False, accept=None, header=None, path_info=None, - custom_predicates=(), context=None, decorator=None, - mapper=None, http_cache=None, match_param=None): - self.name = name - self.request_type = request_type - self.context = context or for_ - self.permission = permission - self.route_name = route_name - self.request_method = request_method - self.request_param = request_param - self.containment = containment - self.attr = attr - self.renderer = renderer - self.wrapper = wrapper - self.xhr = xhr - self.accept = accept - self.header = header - self.path_info = path_info - self.custom_predicates = custom_predicates - self.decorator = decorator - self.mapper = mapper - self.http_cache = http_cache - self.match_param = match_param + def __init__(self, name=default, request_type=default, for_=default, + permission=default, route_name=default, + request_method=default, request_param=default, + containment=default, attr=default, renderer=default, + wrapper=default, xhr=default, accept=default, + header=default, path_info=default, + custom_predicates=default, context=default, + decorator=default, mapper=default, http_cache=default, + match_param=default): + L = locals() + if (context is not default) or (for_ is not default): + L['context'] = context or for_ + for k, v in L.items(): + if k not in ('self', 'L') and v is not default: + setattr(self, k, v) def __call__(self, wrapped): settings = self.__dict__.copy() @@ -215,7 +214,7 @@ class view_config(object): # if the decorator was attached to a method in a class, or # otherwise executed at class scope, we need to set an # 'attr' into the settings if one isn't already in there - if settings['attr'] is None: + if settings.get('attr') is None: settings['attr'] = wrapped.__name__ settings['_info'] = info.codeinfo # fbo "action_method" |
