Package: ophidian
ophiDIanDescriptionophiDIan is a Dependency Injection (DI) container for Python. Unlike other DI
containers that utilize decorators or configuration files to accomplish their
goal, ophiDIan uses type hints to identify and resolve dependencies. In other
words, by using type hinting to resolve dependencies, ophiDIan avoids making
your components dependent upon the DI framework.Ophidian follows theRegister, Resolve, Release (RRR)
pattern.InstallationInstall withpip install ophidianTutorialDependency definitionsFor this tutorial, we'll use the following class definitions:fromabcimportABC,abstractmethodclassAbstractA(ABC):@abstractmethoddefdo_something(self):passclassAbstractB(ABC):@abstractmethoddefdo_something_else(self):passclassDependencyA(AbstractA):defdo_something(self):print(f"{id(self)}: I am dependency A")classDependencyB(AbstractB):def__init__(self,name:str):self._name=namedefdo_something_else(self):print(f"{id(self)}My name is{self._name}")classTestClass1:def__init__(self,a:AbstractA):self.a=aclassTestClass2:__test__=Falsedef__init__(self,b:AbstractB):self.b=bclassTestClass3:def__init__(self,a:AbstractA,b:AbstractB):self.a=aself.b=bRRRThe first step in the RRR pattern is toRegister dependencies. This can be
performed using either theregister()orregister_instance()methods. Next,
objects can be built byResolving. Finally, when the dependency is no
longer needed, it can beReleased.fromophidianimportDIContainerdependency_b_instance=DependencyB("Mike")di_container=DIContainer()di_container.register(AbstractA,DependencyA)di_container.register_instance(AbstractB,dependency_b_instance)test_class_1=di_container.resolve(TestClass1)test_class_2=di_container.resolve(TestClass2)test_class_3=di_container.resolve(TestClass3)assertid(test_class_1.a)!=id(test_class_3.a)assertid(test_class_2.b)==id(dependency_b_instance)assertid(test_class_2.b)==id(test_class_3.b)assertisinstance(test_class_1.a,AbstractA)assertisinstance(test_class_1.a,DependencyA)assertisinstance(test_class_2.b,AbstractB)assertisinstance(test_class_2.b,DependencyB)# Note: All dependencies will automatically be released when the `di_container`#       instance is cleaned up by the garbage collector.di_container.release(AbstractA)di_container.release(AbstractB)In the above example, theDIContainer.register()method is used tell the DI
container that components that depend uponAbstractAshould be supplied a new
instance of the concrete classDependencyA. TheDiContainer.register_instance()method is used to tell the DI container that
all components that depend uponAbstractBshould be supplied with the same
instance ofDependencyB.ConventionsConventions are mainly used to resolve primitive dependencies. For example, if
a component depends upon a file path for a configuration file passed as a
string, a convention can be registered to provide the dependency.fromophidianimportDIContainerclassConfiguration:def__init__(self,a:DependencyA,config_file_path:str):...di_container=DIContainer()di_container.register(AbstractA,DependencyA)di_container.register_convention(str,"config_file_path","/tmp/my_config_file")configuration=di_container.resolve(Configuration)di_container.release(AbstractA)di_container.release_convention(str,"config_file_path")We wouldn't want to useregister()orregister_instance(), as we wouldn't
want all string dependencies to be resolved with the configuration file path.
Instead, the DI container is informed about an established "convention" within
the code: components that depend upon the configuration file path expect a
string parameter named "config_file_path". SeePrimitive Dependencies by Mark
Seemannfor more
information about conventions.DocumentationDIContainerclassDIContainer()A dependency injection (DI) container that uses type annotations to resolve and
inject dependencies.__init__def__init__()register@no_type_checkdefregister(interface:Type[T],concrete_type:Type[T])Register a concretetypethat satisfies a given interface.:param interface: An interface or abstract base class that other classes depend
upon:param concrete_type: Atype(class) that implementsinterface:raises TypeError: Ifconcrete_typeis not a class, or not a subclass ofinterfaceregister_instance@no_type_checkdefregister_instance(interface:Type[T],instance:T)Register a concrete instance that satisfies a given interface.:param interface: An interface or abstract base class that other classes depend
upon:param instance: An instance (object) of atypethat implementsinterface:raises TypeError: Ifinstanceis not an instance ofinterfaceregister_convention@no_type_checkdefregister_convention(type_:Type[T],name:str,instance:T)Register an instance as a conventionAt times — particularly when dealing with primitive types — it can be useful to
define a convention for how dependencies should be resolved. For example, you
might want any class that specifieshostname: strin its constructor to
receive the hostname of the system it's running on. Registering a convention
allows you to assign an object instance to a type, name pair.Example:class TestClass:
        def __init__(self, hostname: str):
            self.hostname = hostname

    di_container = DIContainer()
    di_container.register_convention(str, "hostname", "my_hostname.domain")

    test = di_container.resolve(TestClass)
    assert test.hostname == "my_hostname.domain":param type_: Thetype(class) of the dependency:param name: The name of the dependency parameter:param instance: An instance (object) oftype_that will be injected into constructors
that specify[name]: [type_]as parametersErrorsUnresolvableDependencyErrorclassUnresolvableDependencyError(ValueError):Raised when one or more dependencies cannot be successfully resolved.Running the testsRunning the tests is as simple aspoetry install && poetry run pytest