summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpjenvey <devnull@localhost>2006-12-01 23:54:25 +0000
committerpjenvey <devnull@localhost>2006-12-01 23:54:25 +0000
commit4025ab928d8c3e5e0647c659413989e6d73975a5 (patch)
tree50db5658e5d9d5ea5f4c7bced89fec56b705655d
parent46343c9ca6e82d9623997077bb1c21aecb6e0b4e (diff)
downloadpaste-4025ab928d8c3e5e0647c659413989e6d73975a5.tar.gz
StackedObjectRestorer tests. fixed a couple edge cases:
o get_saved_proxied_obj needed to delve down to allow access to older (overlaid) RegistryManagers' objects (test_restorer_nested_middleman) o save_registry_state needed to save all state, so restoration can get to every overlaid RegistryManagers' objects (test_restorer_middlemen_nested_evalexception). cleaned it up to do all the saving at once, instead of piecemeal
-rw-r--r--paste/registry.py50
-rw-r--r--tests/test_registry.py84
2 files changed, 112 insertions, 22 deletions
diff --git a/paste/registry.py b/paste/registry.py
index ffc853f..57bd0c4 100644
--- a/paste/registry.py
+++ b/paste/registry.py
@@ -374,42 +374,52 @@ class StackedObjectRestorer(object):
self.evalcontext_id = threadinglocal.local()
def save_registry_state(self, environ):
- """Save the current state (top of the stack) of the registry to the
- saved_registry_states dict, keyed by the request's unique identifier"""
+ """Save the state of this request's registry (if it hasn't already been
+ saved) to the saved_registry_states dict, keyed by the request's unique
+ identifier"""
registry = environ.get('paste.registry')
- if not registry:
- return
- if not len(registry.reglist):
- # No state to save
+ if not registry or not len(registry.reglist) or \
+ get_request_id(environ) in self.saved_registry_states:
+ # No Registry, no state to save, or this request's state has
+ # already been saved
return
- # The current level of the stack to be saved
- saved_reglist = registry.reglist[-1]
- for stacked, obj in saved_reglist.itervalues():
- # Tweak the StackedObjectProxies we want to save state for --
- # change the _current_obj stategy to search for the original
- # proxied object when ran from EvalException
- if '_current_obj' not in stacked.__dict__:
- self.enable_restoration(stacked)
+ self.saved_registry_states[get_request_id(environ)] = \
+ registry.reglist[:]
- # prepend instead of append: we're gathering the Registry stack in the
- # opposite direction
- self.saved_registry_states.setdefault(get_request_id(environ),
- []).insert(0, saved_reglist)
+ # Tweak the StackedObjectProxies we want to save state for -- change
+ # the _current_obj stategy to search for the original proxied object
+ # when ran from EvalException
+ for reglist in registry.reglist:
+ for stacked, obj in reglist.itervalues():
+ self.enable_restoration(stacked)
def get_saved_proxied_obj(self, stacked, request_id):
"""Retrieve the saved object proxied by the specified
StackedObjectProxy for the request identified by request_id"""
# All state for the request identifed by request_id
reglists = self.saved_registry_states[request_id]
+
# The top of the stack was current when the exception occurred
- top_reglist = reglists[-1]
- return top_reglist[id(stacked)][1]
+ stack_level = -1
+ reglist = reglists[stack_level]
+ stacked_id = id(stacked)
+ # The StackedObjectProxy may not have been registered by the
+ # RegistryManager that was active when the exception was raised. If it
+ # wasn't, continue searching down the stack until it's found
+ while stacked_id not in reglist and -stack_level <= len(reglists):
+ stack_level -= 1
+ reglist = reglists[stack_level]
+ return reglist[id(stacked)][1]
def enable_restoration(self, stacked):
"""Replace the specified StackedObjectProxy's _current_obj method with
_current_obj_evalexception: forces recovery of the saved proxied object
during EvalException's EvalContext call"""
+ if '_current_obj' in stacked.__dict__:
+ # Restoration already enabled
+ return
+
orig_current_obj = stacked._current_obj
def _current_obj_evalexception(self):
request_id = restorer.in_evalcontext()
diff --git a/tests/test_registry.py b/tests/test_registry.py
index c2538e3..f1eeb28 100644
--- a/tests/test_registry.py
+++ b/tests/test_registry.py
@@ -4,7 +4,9 @@
import py.test
from paste.fixture import *
-from paste.registry import *
+from paste.registry import StackedObjectProxy, RegistryManager, restorer, \
+ get_request_id
+from paste.evalexception.middleware import EvalException
testobj = StackedObjectProxy()
secondobj = StackedObjectProxy(default=dict(hi='people'))
@@ -29,13 +31,16 @@ def simpleapp_withregistry_default(environ, start_response):
class RegistryUsingApp(object):
- def __init__(self, var, value):
+ def __init__(self, var, value, raise_exc=False):
self.var = var
self.value = value
+ self.raise_exc = raise_exc
def __call__(self, environ, start_response):
if environ.has_key('paste.registry'):
environ['paste.registry'].register(self.var, self.value)
+ if self.raise_exc:
+ raise self.raise_exc
status = '200 OK'
response_headers = [('Content-type','text/plain')]
start_response(status, response_headers)
@@ -160,3 +165,78 @@ def test_iterating_response():
assert "{'hi': 'people'}" in res
assert "InsertValue at depth 0 is {'bye': 'friends'}" in res
assert "AppendValue at depth 0 is {'bye': 'friends'}" in res
+
+def _test_restorer(stack, data):
+ extra_environ={'paste.throw_errors': False}
+ request_id = get_request_id(extra_environ)
+ app = TestApp(stack)
+ res = app.get('/', extra_environ=extra_environ, expect_errors=True)
+
+ # Ensure all the StackedObjectProxies are empty after the RegistryUsingApp
+ # raises an Exception
+ for stacked, proxied_obj in data:
+ only_key = proxied_obj.keys()[0]
+ try:
+ assert only_key not in stacked
+ except TypeError:
+ # Definitely empty
+ pass
+
+ # Ensure the StackedObjectProxy data 'works' in the simulated EvalException
+ # context
+ restorer.evalcontext_begin(request_id)
+ try:
+ for stacked, proxied_obj in data:
+ only_key, only_val = proxied_obj.items()[0]
+ assert only_key in stacked and stacked[only_key] == only_val
+ finally:
+ restorer.evalcontext_end()
+
+def _restorer_data():
+ d = [(StackedObjectProxy(name='first'), dict(top='of the registry stack')),
+ (StackedObjectProxy(name='second'), dict(middle='of the stack')),
+ (StackedObjectProxy(name='third'), dict(bottom='of the stack'))]
+ return d
+
+def test_restorer_basic():
+ data = _restorer_data()[0]
+ wsgiapp = RegistryUsingApp(data[0], data[1], raise_exc=Exception())
+ wsgiapp = RegistryManager(wsgiapp)
+ wsgiapp = EvalException(wsgiapp)
+ _test_restorer(wsgiapp, [data])
+
+def test_restorer_basic_manager_outside():
+ data = _restorer_data()[0]
+ wsgiapp = RegistryUsingApp(data[0], data[1], raise_exc=Exception())
+ wsgiapp = EvalException(wsgiapp)
+ wsgiapp = RegistryManager(wsgiapp)
+ _test_restorer(wsgiapp, [data])
+
+def test_restorer_middleman_nested_evalexception():
+ data = _restorer_data()[:2]
+ wsgiapp = RegistryUsingApp(data[0][0], data[0][1], raise_exc=Exception())
+ wsgiapp = EvalException(wsgiapp)
+ wsgiapp = RegistryMiddleMan(wsgiapp, data[1][0], data[1][1], 0)
+ wsgiapp = RegistryManager(wsgiapp)
+ _test_restorer(wsgiapp, data)
+
+def test_restorer_nested_middleman():
+ data = _restorer_data()[:2]
+ wsgiapp = RegistryUsingApp(data[0][0], data[0][1], raise_exc=Exception())
+ wsgiapp = RegistryManager(wsgiapp)
+ wsgiapp = RegistryMiddleMan(wsgiapp, data[1][0], data[1][1], 0)
+ wsgiapp = EvalException(wsgiapp)
+ wsgiapp = RegistryManager(wsgiapp)
+ _test_restorer(wsgiapp, data)
+
+def test_restorer_middlemen_nested_evalexception():
+ data = _restorer_data()
+ wsgiapp = RegistryUsingApp(data[0][0], data[0][1], raise_exc=Exception())
+ wsgiapp = RegistryManager(wsgiapp)
+ wsgiapp = EvalException(wsgiapp)
+ wsgiapp = RegistryMiddleMan(wsgiapp, data[1][0], data[1][1], 0)
+ wsgiapp = RegistryManager(wsgiapp)
+ wsgiapp = RegistryMiddleMan(wsgiapp, data[2][0], data[2][1], 1)
+ wsgiapp = RegistryManager(wsgiapp)
+ _test_restorer(wsgiapp, data)
+