diff options
author | pjenvey <devnull@localhost> | 2006-12-01 23:54:25 +0000 |
---|---|---|
committer | pjenvey <devnull@localhost> | 2006-12-01 23:54:25 +0000 |
commit | 4025ab928d8c3e5e0647c659413989e6d73975a5 (patch) | |
tree | 50db5658e5d9d5ea5f4c7bced89fec56b705655d | |
parent | 46343c9ca6e82d9623997077bb1c21aecb6e0b4e (diff) | |
download | paste-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.py | 50 | ||||
-rw-r--r-- | tests/test_registry.py | 84 |
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) + |