Created
May 18, 2012 00:05
-
-
Save progrium/2722360 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class GlobalContext(object): | |
| """Context manager mixin for stackable singletons | |
| Use this mixin when a class has a global singleton set somewhere that can | |
| be temporarily set while in the context of an instance of that class:: | |
| class Foo(GlobalContext): | |
| instance = None # where we'll keep the singleton | |
| singleton_attr = (Foo, 'instance') # tell mixin where it is | |
| Foo.instance = Foo() # set an initial Foo singleton | |
| temporary_foo = Foo() # create another Foo | |
| # now use it as a context | |
| with temporary_foo: | |
| # the singleton will be set to this instance | |
| assert Foo.instance is temporary_foo | |
| # then set back when you exit the context | |
| assert Foo.instance is not temporary_foo | |
| You can also nest global contexts if necessary. The main API is just | |
| setting where the singleton is with `singleton_attr`, which is a tuple of | |
| (object, attribute name). If `singleton_attr` is not set, there is no | |
| effect when you use the context manager. You can define `singleton_attr` | |
| outside the class definition to decouple your class definition from your | |
| use of a singleton. For example:: | |
| class Foo(GlobalContext): | |
| pass | |
| singleton = Foo() # module level singleton | |
| Foo.singleton_attr = (sys.modules[__name__], 'singleton') | |
| """ | |
| singleton_attr = None | |
| _singleton_stacks = {} | |
| @classmethod | |
| def _get_singleton(cls): | |
| if cls.singleton_attr: | |
| return getattr(*cls.singleton_attr) | |
| @classmethod | |
| def _set_singleton(cls, value): | |
| if cls.singleton_attr: | |
| setattr(*list(cls.singleton_attr)+[value]) | |
| @classmethod | |
| def _push_context(cls, obj): | |
| if cls.singleton_attr: | |
| klass = cls.__name__ | |
| if klass not in cls._singleton_stacks: | |
| cls._singleton_stacks[klass] = [] | |
| cls._singleton_stacks[klass].append(cls._get_singleton()) | |
| cls._set_singleton(obj) | |
| @classmethod | |
| def _pop_context(cls): | |
| if cls.singleton_attr: | |
| klass = cls.__name__ | |
| cls._set_singleton( | |
| cls._singleton_stacks.get(klass, []).pop()) | |
| def __enter__(self): | |
| self.__class__._push_context(self) | |
| return self | |
| def __exit__(self, type, value, traceback): | |
| self.__class__._pop_context() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import unittest | |
| from ginkgo import util | |
| class GlobalContextTest(unittest.TestCase): | |
| singleton_a = None | |
| singleton_b = None | |
| def setUp(self): | |
| GlobalContextTest.singleton_a = object() | |
| GlobalContextTest.singleton_b = object() | |
| def test_push_pop_of_singleton(self): | |
| class TestContext(util.GlobalContext): | |
| singleton_attr = (GlobalContextTest, 'singleton_a') | |
| def _singleton_id(): | |
| return id(GlobalContextTest.singleton_a) | |
| original_id = _singleton_id() | |
| new_object = object() | |
| TestContext._push_context(new_object) | |
| assert not _singleton_id() == original_id | |
| assert _singleton_id() == id(new_object) | |
| TestContext._pop_context() | |
| assert not _singleton_id() == id(new_object) | |
| assert _singleton_id() == original_id | |
| def test_nested_push_pop_of_two_singletons(self): | |
| class TestContext(util.GlobalContext): | |
| singleton_attr = (GlobalContextTest, 'singleton_a') | |
| def _singleton_id(): | |
| return id(GlobalContextTest.singleton_a) | |
| original_id = _singleton_id() | |
| first_object = object() | |
| second_object = object() | |
| assert _singleton_id() == original_id | |
| TestContext._push_context(first_object) | |
| assert _singleton_id() == id(first_object) | |
| TestContext._push_context(second_object) | |
| assert _singleton_id() == id(second_object) | |
| TestContext._pop_context() | |
| assert _singleton_id() == id(first_object) | |
| TestContext._pop_context() | |
| assert _singleton_id() == original_id | |
| def test_multiple_global_contexts(self): | |
| class FirstContext(util.GlobalContext): | |
| singleton_attr = (GlobalContextTest, 'singleton_a') | |
| class SecondContext(util.GlobalContext): | |
| singleton_attr = (GlobalContextTest, 'singleton_b') | |
| def _first_singleton_id(): | |
| return id(GlobalContextTest.singleton_a) | |
| def _second_singleton_id(): | |
| return id(GlobalContextTest.singleton_b) | |
| first_original_id = _first_singleton_id() | |
| second_original_id = _second_singleton_id() | |
| assert not first_original_id == second_original_id | |
| first_object = object() | |
| second_object = object() | |
| FirstContext._push_context(first_object) | |
| assert _first_singleton_id() == id(first_object) | |
| assert not _second_singleton_id() == id(first_object) | |
| SecondContext._push_context(second_object) | |
| assert _second_singleton_id() == id(second_object) | |
| assert not _first_singleton_id() == id(second_object) | |
| assert _first_singleton_id() == id(first_object) | |
| FirstContext._pop_context() | |
| assert _first_singleton_id() == first_original_id | |
| SecondContext._pop_context() | |
| assert _second_singleton_id() == second_original_id | |
| def test_context_manager(self): | |
| class TestContext(util.GlobalContext): pass | |
| TestContext.singleton_attr = (GlobalContextTest, 'singleton_a') | |
| GlobalContextTest.singleton_a = TestContext() | |
| original_id = id(GlobalContextTest.singleton_a) | |
| new_context = TestContext() | |
| with new_context: | |
| assert not original_id == id(GlobalContextTest.singleton_a) | |
| assert id(new_context) == id(GlobalContextTest.singleton_a) | |
| assert original_id == id(GlobalContextTest.singleton_a) | |
| def test_nested_context_managers(self): | |
| class TestContext(util.GlobalContext): pass | |
| TestContext.singleton_attr = (GlobalContextTest, 'singleton_a') | |
| GlobalContextTest.singleton_a = TestContext() | |
| original_id = id(GlobalContextTest.singleton_a) | |
| first_context = TestContext() | |
| second_context = TestContext() | |
| with first_context: | |
| assert id(first_context) == id(GlobalContextTest.singleton_a) | |
| with second_context: | |
| assert id(second_context) == id(GlobalContextTest.singleton_a) | |
| assert id(first_context) == id(GlobalContextTest.singleton_a) | |
| assert original_id == id(GlobalContextTest.singleton_a) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment