{"id":34032647,"url":"https://github.com/guardicode/ophidian","last_synced_at":"2026-04-08T13:31:51.386Z","repository":{"id":196919506,"uuid":"697528321","full_name":"guardicode/ophidian","owner":"guardicode","description":"A dependency injection container for Python","archived":false,"fork":false,"pushed_at":"2023-11-22T13:46:25.000Z","size":701,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-09-21T12:26:46.598Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/guardicode.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-09-27T23:21:36.000Z","updated_at":"2024-07-17T06:44:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"d6c3de44-c0da-4eb8-9893-7467a4910717","html_url":"https://github.com/guardicode/ophidian","commit_stats":null,"previous_names":["guardicode/ophidian"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/guardicode/ophidian","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guardicode%2Fophidian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guardicode%2Fophidian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guardicode%2Fophidian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guardicode%2Fophidian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/guardicode","download_url":"https://codeload.github.com/guardicode/ophidian/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/guardicode%2Fophidian/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31558380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T10:21:54.569Z","status":"ssl_error","status_checked_at":"2026-04-08T10:21:38.171Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-13T18:55:31.313Z","updated_at":"2026-04-08T13:31:51.377Z","avatar_url":"https://github.com/guardicode.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ophiDIan\n\n\u003cdiv align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/guardicode/ophidian/raw/main/images/mascot.png\", width=\"300\"\u003e\n\u003c/div\u003e\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n[![PyPI version](https://badge.fury.io/py/ophidian.svg)](http://badge.fury.io/py/ophidian)\n![Python Version from PEP 621 TOML](https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fraw.githubusercontent.com%2Fguardicode%2Fophidian%2Fmain%2Fpyproject.toml)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat\u0026labelColor=ef8336)](https://timothycrosley.github.io/isort/)\n[![License](https://img.shields.io/pypi/l/ophidian)](https://img.shields.io/pypi/l/ophidian)\n[![GitHub pull requests](https://img.shields.io/github/issues-pr/guardicode/ophidian)](https://img.shields.io/github/issues-pr/guardicode/ophidian)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit\u0026logoColor=white)](https://github.com/pre-commit/pre-commit)\n[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)\n[![codecov](https://codecov.io/gh/guardicode/ophidian/branch/main/graph/badge.svg)](https://codecov.io/gh/guardicode/ophidian)\n\n\u003c/div\u003e\n\n---\n## Description\n\nophiDIan is a Dependency Injection (DI) container for Python. Unlike other DI\ncontainers that utilize decorators or configuration files to accomplish their\ngoal, ophiDIan uses type hints to identify and resolve dependencies. In other\nwords, by using type hinting to resolve dependencies, ophiDIan avoids making\nyour components dependent upon the DI framework.\n\nOphidian follows the [Register, Resolve, Release (RRR)\npattern](https://blog.ploeh.dk/2010/09/29/TheRegisterResolveReleasepattern/).\n\n## Installation\n\nInstall with `pip install ophidian`\n\n## Tutorial\n### Dependency definitions\n\nFor this tutorial, we'll use the following class definitions:\n\n```python\nfrom abc import ABC, abstractmethod\n\n\nclass AbstractA(ABC):\n    @abstractmethod\n    def do_something(self):\n        pass\n\n\nclass AbstractB(ABC):\n    @abstractmethod\n    def do_something_else(self):\n        pass\n\n\nclass DependencyA(AbstractA):\n    def do_something(self):\n        print(f\"{id(self)}: I am dependency A\")\n\n\nclass DependencyB(AbstractB):\n    def __init__(self, name: str):\n        self._name = name\n    def do_something_else(self):\n        print(f\"{id(self)} My name is {self._name}\")\n\n\nclass TestClass1:\n    def __init__(self, a: AbstractA):\n        self.a = a\n\n\nclass TestClass2:\n    __test__ = False\n\n    def __init__(self, b: AbstractB):\n        self.b = b\n\n\nclass TestClass3:\n    def __init__(self, a: AbstractA, b: AbstractB):\n        self.a = a\n        self.b = b\n```\n\n### RRR\n\nThe first step in the RRR pattern is to *R*egister dependencies. This can be\nperformed using either the `register()` or `register_instance()` methods. Next,\nobjects can be built by *R*esolving. Finally, when the dependency is no\nlonger needed, it can be *R*eleased.\n\n```python\nfrom ophidian import DIContainer\n\ndependency_b_instance = DependencyB(\"Mike\")\n\ndi_container = DIContainer()\ndi_container.register(AbstractA, DependencyA)\ndi_container.register_instance(AbstractB, dependency_b_instance)\n\ntest_class_1 = di_container.resolve(TestClass1)\ntest_class_2 = di_container.resolve(TestClass2)\ntest_class_3 = di_container.resolve(TestClass3)\n\nassert id(test_class_1.a) != id(test_class_3.a)\nassert id(test_class_2.b) == id(dependency_b_instance)\nassert id(test_class_2.b) == id(test_class_3.b)\nassert isinstance(test_class_1.a, AbstractA)\nassert isinstance(test_class_1.a, DependencyA)\nassert isinstance(test_class_2.b, AbstractB)\nassert isinstance(test_class_2.b, DependencyB)\n\n# Note: All dependencies will automatically be released when the `di_container`\n#       instance is cleaned up by the garbage collector.\ndi_container.release(AbstractA)\ndi_container.release(AbstractB)\n```\n\nIn the above example, the `DIContainer.register()` method is used tell the DI\ncontainer that components that depend upon `AbstractA` should be supplied a new\ninstance of the concrete class `DependencyA`. The\n`DiContainer.register_instance()` method is used to tell the DI container that\nall components that depend upon `AbstractB` should be supplied with the same\ninstance of `DependencyB`.\n\n### Conventions\n\nConventions are mainly used to resolve primitive dependencies. For example, if\na component depends upon a file path for a configuration file passed as a\nstring, a convention can be registered to provide the dependency.\n\n```python\nfrom ophidian import DIContainer\n\nclass Configuration:\n    def __init__(self, a: DependencyA, config_file_path: str):\n        ...\n\ndi_container = DIContainer()\ndi_container.register(AbstractA, DependencyA)\ndi_container.register_convention(str, \"config_file_path\", \"/tmp/my_config_file\")\n\nconfiguration = di_container.resolve(Configuration)\n\ndi_container.release(AbstractA)\ndi_container.release_convention(str, \"config_file_path\")\n```\n\nWe wouldn't want to use `register()` or `register_instance()`, as we wouldn't\nwant all string dependencies to be resolved with the configuration file path.\nInstead, the DI container is informed about an established \"convention\" within\nthe code: components that depend upon the configuration file path expect a\nstring parameter named \"config_file_path\". See [Primitive Dependencies by Mark\nSeemann](https://blog.ploeh.dk/2012/07/02/PrimitiveDependencies/) for more\ninformation about conventions.\n\n## Documentation\n### DIContainer\n\n```python\nclass DIContainer()\n```\n\nA dependency injection (DI) container that uses type annotations to resolve and\ninject dependencies.\n\n#### \\_\\_init\\_\\_\n\n```python\ndef __init__()\n```\n\n#### register\n\n```python\n@no_type_check\ndef register(interface: Type[T], concrete_type: Type[T])\n```\n\nRegister a concrete `type` that satisfies a given interface.\n\n:param interface: An interface or abstract base class that other classes depend\nupon\u003c/br\u003e\n:param concrete_type: A `type` (class) that implements `interface`\u003c/br\u003e\n:raises TypeError: If `concrete_type` is not a class, or not a subclass of\n`interface`\n\n\n#### register\\_instance\n\n```python\n@no_type_check\ndef register_instance(interface: Type[T], instance: T)\n```\n\nRegister a concrete instance that satisfies a given interface.\n\n:param interface: An interface or abstract base class that other classes depend\nupon\u003c/br\u003e\n:param instance: An instance (object) of a `type` that implements\n`interface`\u003c/br\u003e\n:raises TypeError: If `instance` is not an instance of `interface`\n\n\n#### register\\_convention\n\n```python\n@no_type_check\ndef register_convention(type_: Type[T], name: str, instance: T)\n```\n\nRegister an instance as a convention\n\nAt times — particularly when dealing with primitive types — it can be useful to\ndefine a convention for how dependencies should be resolved. For example, you\nmight want any class that specifies `hostname: str` in its constructor to\nreceive the hostname of the system it's running on. Registering a convention\nallows you to assign an object instance to a type, name pair.\n\n**Example:**\n\n        class TestClass:\n            def __init__(self, hostname: str):\n                self.hostname = hostname\n\n        di_container = DIContainer()\n        di_container.register_convention(str, \"hostname\", \"my_hostname.domain\")\n\n        test = di_container.resolve(TestClass)\n        assert test.hostname == \"my_hostname.domain\"\n\n:param type_: The `type` (class) of the dependency\u003c/br\u003e\n:param name: The name of the dependency parameter\u003c/br\u003e\n:param instance: An instance (object) of `type_` that will be injected into constructors\n                 that specify `[name]: [type_]` as parameters\n\n### Errors\n#### UnresolvableDependencyError\n\n```python\nclass UnresolvableDependencyError(ValueError):\n```\n\nRaised when one or more dependencies cannot be successfully resolved.\n\n## Running the tests\n\nRunning the tests is as simple as `poetry install \u0026\u0026 poetry run pytest`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguardicode%2Fophidian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fguardicode%2Fophidian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fguardicode%2Fophidian/lists"}