Skip to main content

A dependency injection container for Python

Project description

ophiDIan


PyPI version Python Version from PEP 621 TOML Code style: black Imports: isort License GitHub pull requests pre-commit Checked with mypy codecov


Description

ophiDIan 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 the Register, Resolve, Release (RRR) pattern

Tutorial

Dependency definitions

For this tutorial, we'll use the following class definitions:

from abc import ABC, abstractmethod


class AbstractA(ABC):
    @abstractmethod
    def do_something(self):
        pass


class AbstractB(ABC):
    @abstractmethod
    def do_something_else(self):
        pass


class DependencyA(AbstractA):
    def do_something(self):
        print(f"{id(self)}: I am dependency A")


class DependencyB(AbstractB):
    def __init__(self, name: str):
        self._name = name
    def do_something_else(self):
        print(f"{id(self)} My name is {self._name}")


class TestClass1:
    def __init__(self, a: AbstractA):
        self.a = a


class TestClass2:
    __test__ = False

    def __init__(self, b: AbstractB):
        self.b = b


class TestClass3:
    def __init__(self, a: AbstractA, b: AbstractB):
        self.a = a
        self.b = b

RRR

The first step in the RRR pattern is to Register dependencies. This can be performed using either the register() or register_instance() methods. Next, objects can be built by Resolving. Finally, when the dependency is no longer needed, it can be released.

from ophidian import DIContainer

di_container = DIContainer()
di_container.register(AbstractA, DependencyA)

my_dependency_b = DependencyB("Mike")
di_container.register(AbstractA, DependencyA)
di_container.register_instance(AbstractB, my_dependency_b)

test_class_1 = di_container.resolve(TestClass1)
test_class_2 = di_container.resolve(TestClass2)
test_class_3 = di_container.resolve(TestClass3)

assert id(test_class_1.a) != id(test_class_3.a)
assert id(test_class_2.b) == id(test_class_3.b)
assert isinstance(test_class_1.a, AbstractA)
assert isinstance(test_class_1.a, DependencyA)
assert isinstance(test_class_2.b, AbstractB)
assert isinstance(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, the DIContainer.register() method is used tell the DI container that components that depend upon AbstractA should be supplied a new instance of the concrete class DependencyA. The DiContainer.register_instance() method is used to tell the DI container that all components that depend upon AbstractB should be supplied with the same instance of DependencyB.

Conventions

Conventions 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.

from ophidian import DIContainer

class Configuration:
    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 use register() or register_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". See Primitive Dependencies by Mark Seemann for more information about conventions.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ophidian_di-0.1.0.post1.tar.gz (18.0 kB view details)

Uploaded Source

Built Distribution

ophidian_di-0.1.0.post1-py3-none-any.whl (20.7 kB view details)

Uploaded Python 3

File details

Details for the file ophidian_di-0.1.0.post1.tar.gz.

File metadata

  • Download URL: ophidian_di-0.1.0.post1.tar.gz
  • Upload date:
  • Size: 18.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.5.1 CPython/3.10.12 Linux/5.15.0-83-generic

File hashes

Hashes for ophidian_di-0.1.0.post1.tar.gz
Algorithm Hash digest
SHA256 446be089fcf05f129557b449c92441779f964743eaf2a241feb135782f76ae65
MD5 c441fc7856c546dbf7b66f16f653d167
BLAKE2b-256 fe29f23c6027b882a79454aa46ad9dba4c85ef09119728502bfe1c265e2f9567

See more details on using hashes here.

File details

Details for the file ophidian_di-0.1.0.post1-py3-none-any.whl.

File metadata

  • Download URL: ophidian_di-0.1.0.post1-py3-none-any.whl
  • Upload date:
  • Size: 20.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.5.1 CPython/3.10.12 Linux/5.15.0-83-generic

File hashes

Hashes for ophidian_di-0.1.0.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 56f9b104e44a8f745c8f8484fcb896c9ad801328c9f980107eb81404523f9f53
MD5 a7f558da44275d37db8abf25df3d500e
BLAKE2b-256 714a646c715ab79345fa965eea9bf118f30df1b5222ffe1baa3fe9f56e2d4187

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page