diff options
| -rw-r--r-- | CHANGES.txt | 21 | ||||
| -rw-r--r-- | docs/whatsnew-1.1.rst | 16 | ||||
| -rw-r--r-- | pyramid/events.py | 45 | ||||
| -rw-r--r-- | pyramid/interfaces.py | 13 | ||||
| -rw-r--r-- | pyramid/tests/test_events.py | 21 |
5 files changed, 85 insertions, 31 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 2dead04b4..b9e645a38 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,24 @@ +Next Release +============ + +Behavior Changes +---------------- + +- Previously, If a ``BeforeRender`` event subscriber added a value via the + ``__setitem__`` or ``update`` methods of the event object with a key that + already existed in the renderer globals dictionary, a ``KeyError`` was + raised. With the deprecation of the "add_renderer_globals" feature of the + configurator, there was no way to override an existing value in the + renderer globals dictionary that already existed. Now, the event object + will overwrite an older value that is already in the globals dictionary + when its ``__setitem__`` or ``update`` is called (as well as the new + ``setdefault`` method), just like a plain old dictionary. As a result, for + maximum interoperability with other third-party subscribers, if you write + an event subscriber meant to be used as a BeforeRender subscriber, your + subscriber code will now need to (using ``.get`` or ``__contains__`` of the + event object) ensure no value already exists in the renderer globals + dictionary before setting an overriding value. + 1.1b1 (2011-07-10) ================== diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst index 8cf6c715c..345cbfa30 100644 --- a/docs/whatsnew-1.1.rst +++ b/docs/whatsnew-1.1.rst @@ -481,6 +481,22 @@ Deprecations and Behavior Differences def expects_object_event(object, event): print object, event +- In 1.0, if a :class:`pyramid.events.BeforeRender` event subscriber added a + value via the ``__setitem__`` or ``update`` methods of the event object + with a key that already existed in the renderer globals dictionary, a + ``KeyError`` was raised. With the deprecation of the + "add_renderer_globals" feature of the configurator, there was no way to + override an existing value in the renderer globals dictionary that already + existed. Now, the event object will overwrite an older value that is + already in the globals dictionary when its ``__setitem__`` or ``update`` is + called (as well as the new ``setdefault`` method), just like a plain old + dictionary. As a result, for maximum interoperability with other + third-party subscribers, if you write an event subscriber meant to be used + as a BeforeRender subscriber, your subscriber code will now need to (using + ``.get`` or ``__contains__`` of the event object) ensure no value already + exists in the renderer globals dictionary before setting an overriding + value. + Dependency Changes ------------------ diff --git a/pyramid/events.py b/pyramid/events.py index 22cbf0cb2..b536bfd67 100644 --- a/pyramid/events.py +++ b/pyramid/events.py @@ -179,35 +179,42 @@ class BeforeRender(dict): event['mykey'] = 'foo' An object of this type is sent as an event just before a :term:`renderer` - is invoked (but *after* the application-level renderer globals factory - added via - :class:`pyramid.config.Configurator.set_renderer_globals_factory`, - if any, has injected its own keys into the renderer globals dictionary). - - If a subscriber attempts to add a key that already exist in the renderer - globals dictionary, a :exc:`KeyError` is raised. This limitation is - enforced because event subscribers do not possess any relative ordering. - The set of keys added to the renderer globals dictionary by all - :class:`pyramid.events.BeforeRender` subscribers and renderer globals - factories must be unique. """ + is invoked (but *after* the -- deprecated -- application-level renderer + globals factory added via + :class:`pyramid.config.Configurator.set_renderer_globals_factory`, if + any, has injected its own keys into the renderer globals dictionary). + + If a subscriber adds a key via ``__setitem__`` or that already exists in + the renderer globals dictionary, it will overwrite an older value that is + already in the globals dictionary. This can be problematic because event + subscribers to the BeforeRender event do not possess any relative + ordering. For maximum interoperability with other third-party + subscribers, if you write an event subscriber meant to be used as a + BeforeRender subscriber, your subscriber code will need to (using + ``.get`` or ``__contains__`` of the event object) ensure no value already + exists in the renderer globals dictionary before setting an overriding + value.""" def __init__(self, system): self._system = system def __setitem__(self, name, value): """ Set a name/value pair into the dictionary which is passed to a - renderer as the renderer globals dictionary. If the ``name`` already - exists in the target dictionary, a :exc:`KeyError` will be raised.""" - if name in self._system: - raise KeyError('%s is already a renderer globals value' % name) + renderer as the renderer globals dictionary.""" self._system[name] = value + def setdefault(self, name, default=None): + """ Return the existing value for ``name`` in the renderers globals + dictionary. If no value with ``name`` exists in the dictionary, set + the ``default`` value into the renderer globals dictionary under the + name passed. If a value already existed in the dictionary, return + it. If a value did not exist in the dictionary, return the default""" + return self._system.setdefault(name, default) + def update(self, d): """ Update the renderer globals dictionary with another dictionary - ``d``. If any of the key names in the source dictionary already exist - in the target dictionary, a :exc:`KeyError` will be raised""" - for k, v in d.items(): - self[k] = v + ``d``.""" + return self._system.update(d) def __contains__(self, k): """ Return ``True`` if ``k`` exists in the renderer globals diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index fee8d549d..4ef58846b 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -284,13 +284,18 @@ class IBeforeRender(Interface): """ def __setitem__(name, value): """ Set a name/value pair into the dictionary which is passed to a - renderer as the renderer globals dictionary. If the ``name`` already - exists in the target dictionary, a :exc:`KeyError` will be raised.""" + renderer as the renderer globals dictionary. """ + + def setdefault(name, default=None): + """ Return the existing value for ``name`` in the renderers globals + dictionary. If no value with ``name`` exists in the dictionary, set + the ``default`` value into the renderer globals dictionary under the + name passed. If a value already existed in the dictionary, return + it. If a value did not exist in the dictionary, return the default""" def update(d): """ Update the renderer globals dictionary with another dictionary - ``d``. If any of the key names in the source dictionary already exist - in the target dictionary, a :exc:`KeyError` will be raised""" + ``d``. """ def __contains__(k): """ Return ``True`` if ``k`` exists in the renderer globals diff --git a/pyramid/tests/test_events.py b/pyramid/tests/test_events.py index 09f5f17ab..e3a10ccad 100644 --- a/pyramid/tests/test_events.py +++ b/pyramid/tests/test_events.py @@ -195,10 +195,20 @@ class TestBeforeRender(unittest.TestCase): event['a'] = 1 self.assertEqual(system, {'a':1}) - def test_setitem_fail(self): - system = {'a':1} + def test_setdefault_fail(self): + system = {} + event = self._makeOne(system) + result = event.setdefault('a', 1) + self.assertEqual(result, 1) + self.assertEqual(system, {'a':1}) + + def test_setdefault_success(self): + system = {} event = self._makeOne(system) - self.assertRaises(KeyError, event.__setitem__, 'a', 1) + event['a'] = 1 + result = event.setdefault('a', 2) + self.assertEqual(result, 1) + self.assertEqual(system, {'a':1}) def test_update_success(self): system = {'a':1} @@ -206,11 +216,6 @@ class TestBeforeRender(unittest.TestCase): event.update({'b':2}) self.assertEqual(system, {'a':1, 'b':2}) - def test_update_fail(self): - system = {'a':1} - event = self._makeOne(system) - self.assertRaises(KeyError, event.update, {'a':1}) - def test__contains__True(self): system = {'a':1} event = self._makeOne(system) |
