Skip to content

Instantly share code, notes, and snippets.

@progrium
Created May 18, 2012 00:05
Show Gist options
  • Select an option

  • Save progrium/2722360 to your computer and use it in GitHub Desktop.

Select an option

Save progrium/2722360 to your computer and use it in GitHub Desktop.
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()
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