summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2012-01-06 02:16:03 -0500
committerChris McDonough <chrism@plope.com>2012-01-06 02:16:14 -0500
commit4c29ef5be6e7bb1418ce896a92f4c8bd6d9a3111 (patch)
tree10dc0af568381e30c4ab5eb6f6f1611c40bbc157
parent683941663033bbac588b9ac8f1ff28eeefc511c9 (diff)
downloadpyramid-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.txt10
-rw-r--r--pyramid/tests/test_config/test_views.py38
-rw-r--r--pyramid/tests/test_view.py40
-rw-r--r--pyramid/view.py55
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"