summaryrefslogtreecommitdiffstats
path: root/di.py
diff options
context:
space:
mode:
authorMartin Sivak <msivak@redhat.com>2012-11-23 16:02:06 +0100
committerMartin Sivak <msivak@redhat.com>2012-11-23 16:02:06 +0100
commit8a2ad82232251a7e91dc01bcefcf21d79ba38b79 (patch)
tree7591e8efc1b7be175b3e86133feb3f7f1dfb8a36 /di.py
parent6cb99c3b69789955983e1a869486058cc3cb7c65 (diff)
downloadpython-di-8a2ad82232251a7e91dc01bcefcf21d79ba38b79.tar.gz
python-di-8a2ad82232251a7e91dc01bcefcf21d79ba38b79.tar.xz
python-di-8a2ad82232251a7e91dc01bcefcf21d79ba38b79.zip
Add setuptools and Fedora infrastructure structure and files
Diffstat (limited to 'di.py')
-rw-r--r--di.py219
1 files changed, 0 insertions, 219 deletions
diff --git a/di.py b/di.py
deleted file mode 100644
index 8ffce38..0000000
--- a/di.py
+++ /dev/null
@@ -1,219 +0,0 @@
-"""This module implements dependency injection mechanisms."""
-
-__author__ = "Martin Sivak <msivak@redhat.com>"
-__all__ = ["DI_ENABLE", "di_enable", "inject", "usesclassinject"]
-
-from functools import wraps, partial
-from types import FunctionType
-
-DI_ENABLE = True
-
-def di_enable(method):
- """This decorator enables DI mechanisms in an environment
- where DI is disabled by default.
-
- Can be used only on methods or simple functions.
- """
- @wraps(method)
- def caller(*args, **kwargs):
- """The replacement method doing the DI enablement.
- """
- global DI_ENABLE
- old = DI_ENABLE
- DI_ENABLE = True
- ret = method(*args, **kwargs)
- DI_ENABLE = old
- return ret
-
- return caller
-
-class DiRegistry(object):
- """This class is the internal core of the DI engine.
- It records the injected objects, handles the execution
- and cleanup tasks associated with the DI mechanisms.
- """
-
- def __init__(self, obj):
- self._obj = obj
- self._used_objects = {}
- self._obj._di_ = self._used_objects
-
- def __get__(self, obj, objtype):
- """Support instance methods."""
- return partial(self.__call__, obj)
-
- def register(self, *args, **kwargs):
- """Add registered injections to the instance of DiRegistry
- """
- self._used_objects.update(kwargs)
- for used_object in args:
- if hasattr(used_object, "__name__"):
- self._used_objects[used_object.__name__] = used_object
- elif isinstance(used_object, basestring):
- pass # it is already global, so this is just an annotation
- else:
- raise ValueError("%s is not a string or object with __name__" % used_object)
-
- def __call__(self, *args, **kwargs):
- if not issubclass(type(self._obj), FunctionType):
- # call constructor or callable class
- # (which use @usesclassinject if needed)
- return self._obj(*args, **kwargs)
- else:
- return di_call(self._used_objects, self._obj,
- *args, **kwargs)
-
-def func_globals(func):
- """Helper method that allows access to globals
- depending on the Python version.
- """
- if hasattr(func, "func_globals"):
- return func.func_globals # Python 2
- else:
- return func.__globals__ # Python 3
-
-def di_call(di_dict, method, *args, **kwargs):
- """This method is the core of dependency injection framework.
- It modifies methods global namespace to define all the injected
- variables, executed the method under test and then restores
- the global namespace back.
-
- This variant is used on plain functions.
-
- The modified global namespace is discarded after the method finishes
- so all new global variables and changes to scalars will be lost.
- """
- # modify the globals
- new_globals = func_globals(method).copy()
- new_globals.update(di_dict)
-
- # create new func with modified globals
- new_method = FunctionType(method.func_code,
- new_globals, method.func_name,
- method.func_defaults, method.func_closure)
-
- # execute the method and return it's ret value
- return new_method(*args, **kwargs)
-
-
-def inject(*args, **kwargs):
- """Decorator that registers all the injections we want to pass into
- a unit possibly under test.
-
- It can be used to decorate class, method or simple function, but
- if it is a decorated class, it's methods has to be decorated with
- @usesinject to use the DI mechanism.
- """
- def inject_decorate(obj):
- """The actual decorator generated by @inject."""
- if not DI_ENABLE:
- return obj
-
- if not isinstance(obj, DiRegistry):
- obj = DiRegistry(obj)
-
- obj.register(*args, **kwargs)
- return obj
-
- return inject_decorate
-
-def usesclassinject(method):
- """This decorator marks a method inside of @inject decorated
- class as a method that should use the dependency injection
- mechanisms.
- """
- if not DI_ENABLE:
- return method
-
- @wraps(method)
- def call(*args, **kwargs):
- """The replacement method acting as a proxy to @inject
- decorated class and it's DI mechanisms."""
- self = args[0]
- return di_call(self._di_, method, *args, **kwargs)
-
- return call
-
-### Unittests are defined below this point
-import unittest
-
-class BareFuncTestCase(unittest.TestCase):
- @inject(injected_func = str.lower)
- def method(self, arg):
- return injected_func(arg)
-
- @inject(injected_func = method)
- def method2(self, arg):
- return injected_func(self, arg)
-
- def test_bare_inject(self):
- """Tests the injection to plain methods."""
- self.assertEqual("a", self.method("A"))
-
- def test_double_inject(self):
- """Tests the injection to two plain methods."""
- self.assertEqual("a", self.method2("A"))
-
- def test_inject_global_tainting(self):
- """Tests whether the global namespace is clean
- after the injection is done."""
- global injected_func
- injected_func = None
- self.method("A")
- self.assertEqual(None, injected_func)
-
-
-@inject(injected_func = str.lower)
-class Test(object):
- """Test fixture for class injection."""
- @usesclassinject
- def method(self, arg):
- return injected_func(arg)
-
-
-@inject(injected_func = str.lower)
-class TestInit(object):
- """Test fixture for injection to __init__."""
- @usesclassinject
- def __init__(self, arg):
- self.value = injected_func(arg)
-
-
-@inject(injected_func = str.lower)
-class TestCallable(object):
- """Test fixture for callable classes."""
- @usesclassinject
- def __call__(self, arg):
- return injected_func(arg)
-
-class TestCallableSingle(object):
- """Test fixture for callable classes with
- simple method injection."""
- @inject(injected_func = str.lower)
- def __call__(self, arg):
- return injected_func(arg)
-
-class ClassDITestCase(unittest.TestCase):
-
- def test_class_inject(self):
- """Test injection to instance method."""
- obj = Test()
- self.assertEqual("a", obj.method("A"))
-
- def test_class_init_inject(self):
- """Test injection to class constructor."""
- obj = TestInit("A")
- self.assertEqual("a", obj.value)
-
- def test_callable_class(self):
- """Test class injection to callable class."""
- obj = TestCallable()
- self.assertEqual("a", obj("A"))
-
- def test_callable_class_single(self):
- """Test method injection to callable class."""
- obj = TestCallableSingle()
- self.assertEqual("a", obj("A"))
-
-if __name__ == "__main__":
- unittest.main()