summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt21
-rw-r--r--docs/whatsnew-1.1.rst16
-rw-r--r--pyramid/events.py45
-rw-r--r--pyramid/interfaces.py13
-rw-r--r--pyramid/tests/test_events.py21
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)