{"id":13458890,"url":"https://github.com/ivankorobkov/python-inject","last_synced_at":"2026-02-24T04:02:10.167Z","repository":{"id":37548375,"uuid":"507394","full_name":"ivankorobkov/python-inject","owner":"ivankorobkov","description":"Python dependency injection","archived":false,"fork":false,"pushed_at":"2025-11-10T08:22:51.000Z","size":516,"stargazers_count":746,"open_issues_count":25,"forks_count":84,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-11-10T10:15:56.639Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ivankorobkov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2010-02-08T06:46:16.000Z","updated_at":"2025-11-10T08:22:56.000Z","dependencies_parsed_at":"2022-07-22T05:01:54.955Z","dependency_job_id":"9fd3749b-0f80-4034-a908-557a791a6b08","html_url":"https://github.com/ivankorobkov/python-inject","commit_stats":{"total_commits":292,"total_committers":28,"mean_commits":"10.428571428571429","dds":"0.21575342465753422","last_synced_commit":"5be9189e65adc1d9b5012d5c5a940dcaa4401a7a"},"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/ivankorobkov/python-inject","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankorobkov%2Fpython-inject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankorobkov%2Fpython-inject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankorobkov%2Fpython-inject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankorobkov%2Fpython-inject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ivankorobkov","download_url":"https://codeload.github.com/ivankorobkov/python-inject/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ivankorobkov%2Fpython-inject/sbom","scorecard":{"id":40162,"data":{"date":"2025-08-04","repo":{"name":"github.com/ivankorobkov/python-inject","commit":"40b9a0cfdb18789c39c5c32a2eeb9701e7b11c64"},"scorecard":{"version":"v5.2.1-28-gc1d103a9","commit":"c1d103a9bb9f635ec7260bf9aa0699466fa4be0e"},"score":5.3,"checks":[{"name":"Code-Review","score":7,"reason":"Found 10/13 approved changesets -- score normalized to 7","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"15 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 27 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-14T21:18:45.425Z","repository_id":37548375,"created_at":"2025-08-14T21:18:45.425Z","updated_at":"2025-08-14T21:18:45.425Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29771040,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T04:01:02.180Z","status":"ssl_error","status_checked_at":"2026-02-24T03:59:49.901Z","response_time":75,"last_error":"SSL_read: 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":"2024-07-31T09:00:59.373Z","updated_at":"2026-02-24T04:02:10.141Z","avatar_url":"https://github.com/ivankorobkov.png","language":"Python","funding_links":[],"categories":["Software","Python"],"sub_categories":["DI Frameworks / Containers"],"readme":"# python-inject [![Build Status](https://travis-ci.org/ivankorobkov/python-inject.svg?branch=master)](https://travis-ci.org/ivankorobkov/python-inject)\nDependency injection the python way, the good way.\n\n## Key features\n* Fast.\n* Thread-safe.\n* Simple to use.\n* Does not steal class constructors.\n* Does not try to manage your application object graph.\n* Transparently integrates into tests.\n* Autoparams leveraging type annotations.\n* Supports type hinting in Python 3.5+.\n* Supports Python 3.9+ (`v5.*`), 3.5-3.8 (`v4.*`) and Python 2.7–3.5 (`v3.*`).\n* Supports context managers.\n\n## Python Support\n\n| Python  | Inject Version |\n|---------|----------------|\n| 3.9+    | 5.0+           |\n| 3.6-3.8 | 4.1+, \u003c 5.0    |\n| 3.5     | 4.0            |\n| \u003c 3.5   | 3.*            |\n\n\n## Installation\nUse pip to install the lastest version:\n\n```bash\npip install inject\n```\n\n## Autoparams example\n`@inject.autoparams` returns a decorator which automatically injects arguments into a function \nthat uses type annotations. This is supported only in Python \u003e= 3.5.\n\n```python\n@inject.autoparams\ndef refresh_cache(cache: RedisCache, db: DbInterface):\n    pass\n```\n\nThere is an option to specify which arguments we want to inject without attempts of \ninjecting everything:\n\n```python\n@inject.autoparams('cache', 'db')\ndef sign_up(name, email, cache: RedisCache, db: DbInterface):\n    pass\n```\n\nIt is also acceptable to use explicit curly braces notation (`@inject.autoparams()`)\nfor non-parameterized decorations — it will be treated the same as `@inject.autoparams`.\n\n## Step-by-step example\n```python\n# Import the inject module.\nimport inject\n\n\n# `inject.instance` requests dependencies from the injector.\ndef foo(bar):\n    cache = inject.instance(Cache)\n    cache.save('bar', bar)\n\n\n# `inject.params` injects dependencies as keyword arguments or positional argument. \n# Also you can use @inject.autoparams in Python 3.5, see the example above.\n@inject.params(cache=Cache, user=CurrentUser)\ndef baz(foo, cache=None, user=None):\n    cache.save('foo', foo, user)\n\n# this can be called in different ways:\n# with injected arguments\nbaz('foo')\n\n# with positional arguments\nbaz('foo', my_cache)\n\n# with keyword arguments\nbaz('foo', my_cache, user=current_user)\n\n\n# `inject.param` is deprecated, use `inject.params` instead.\n@inject.param('cache', Cache)\ndef bar(foo, cache=None):\n    cache.save('foo', foo)\n\n\n# `inject.attr` creates properties (descriptors) which request dependencies on access.\nclass User(object):\n    cache = inject.attr(Cache)\n            \n    def __init__(self, id):\n        self.id = id\n\n    def save(self):\n        self.cache.save('users', self)\n    \n    @classmethod\n    def load(cls, id):\n        return cls.cache.load('users', id)\n\n\n# Create an optional configuration.\ndef my_config(binder):\n    binder.bind(Cache, RedisCache('localhost:1234'))\n\n# Configure a shared injector.\ninject.configure(my_config)\n\n\n# Instantiate User as a normal class. Its `cache` dependency is injected when accessed.\nuser = User(10)\nuser.save()\n\n# Call the functions, the dependencies are automatically injected.\nfoo('Hello')\nbar('world')\n```\n\n\n## Context managers\nBinding a class to an instance of a context manager (through `bind` or `bind_to_constructor`) \nor to a function decorated as a context manager leads to the context manager to be used as is, \nnot via with statement.\n\n```python\n@contextlib.contextmanager\ndef get_file_sync():\n    obj = MockFile()\n    yield obj\n    obj.destroy()\n\n@contextlib.asynccontextmanager\nasync def get_conn_async():\n    obj = MockConnection()\n    yield obj\n    obj.destroy()\n\ndef config(binder):\n    binder.bind_to_provider(MockFile, get_file_sync)\n    binder.bind(int, 100)\n    binder.bind_to_provider(str, lambda: \"Hello\")\n    binder.bind_to_provider(MockConnection, get_conn_sync)\n\n    inject.configure(config)\n\n@inject.autoparams()\ndef example(conn: MockConnection, file: MockFile):\n    # Connection and file will be automatically destroyed on exit.\n    pass\n```\n\n\n## Usage with Django\nDjango can load some modules multiple times which can lead to \n`InjectorException: Injector is already configured`. You can use `configure(once=True)` which\nis guaranteed to run only once when the injector is absent:\n```python\nimport inject\ninject.configure(my_config, once=True)\n```\n\n## Testing\nIn tests use `inject.configure(callable, clear=True)` to create a new injector on setup,\nand optionally `inject.clear()` to clean up on tear down:\n```python\nclass MyTest(unittest.TestCase):\n    def setUp(self):\n        inject.configure(lambda binder: binder\n            .bind(Cache, MockCache()) \\\n            .bind(Validator, TestValidator()),\n            clear=True)\n    \n    def tearDown(self):\n        inject.clear()\n```\n\n## Composable configurations\nYou can reuse configurations and override already registered dependencies to fit the needs \nin different environments or specific tests.\n```python\n    def base_config(binder):\n        # ... more dependencies registered here\n        binder.bind(Validator, RealValidator())\n        binder.bind(Cache, RedisCache('localhost:1234'))\n\n    def tests_config(binder):\n        # reuse existing configuration\n        binder.install(base_config)\n\n        # override only certain dependencies\n        binder.bind(Validator, TestValidator())\n        binder.bind(Cache, MockCache())\n    \n    inject.configure(tests_config, allow_override=True, clear=True)\n        \n```\n\n## Thread-safety\nAfter configuration the injector is thread-safe and can be safely reused by multiple threads.\n\n## Binding types\n**Instance** bindings always return the same instance:\n\n```python\nredis = RedisCache(address='localhost:1234')\ndef config(binder):\n    binder.bind(Cache, redis)\n```\n    \n**Constructor** bindings create a singleton on injection:\n\n```python\ndef config(binder):\n    # Creates a redis cache singleton on first injection.\n    binder.bind_to_constructor(Cache, lambda: RedisCache(address='localhost:1234'))\n```\n\n**Provider** bindings call the provider on injection:\n\n```python\ndef get_my_thread_local_cache():\n    pass\n\ndef config(binder):\n    # Executes the provider on each injection.\n    binder.bind_to_provider(Cache, get_my_thread_local_cache) \n```\n\n**Runtime** bindings automatically create singletons on injection, require no configuration.\nFor example, only the `Config` class binding is present, other bindings are runtime:\n\n```python\nclass Config(object):\n    pass\n\nclass Cache(object):\n    config = inject.attr(Config)\n\nclass Db(object):\n    config = inject.attr(Config)\n\nclass User(object):\n    cache = inject.attr(Cache)\n    db = inject.attr(Db)\n    \n    @classmethod\n    def load(cls, user_id):\n        return cls.cache.load('users', user_id) or cls.db.load('users', user_id)\n    \ninject.configure(lambda binder: binder.bind(Config, load_config_file()))\nuser = User.load(10)\n```\n## Disabling runtime binding\nSometimes runtime binding leads to unexpected behaviour.  Say if you forget\nto bind an instance to a class, `inject` will try to implicitly instantiate it.\n\nIf an instance is unintentionally created with default arguments it may lead to\nhard-to-debug bugs.  To disable runtime binding and make sure that only \nexplicitly bound instances are injected, pass `bind_in_runtime=False` to `inject.configure`.\n\nIn this case `inject` immediately raises `InjectorException` when the code\ntries to get an unbound instance.\n\n## Keys\nIt is possible to use any hashable object as a binding key. For example:\n\n```python\nimport inject\n\ninject.configure(lambda binder: \\\n    binder.bind('host', 'localhost') \\\n    binder.bind('port', 1234))\n```\n\n## Why no scopes?\nI've used Guice and Spring in Java for a lot of years, and I don't like their scopes.\n`python-inject` by default creates objects as singletons. It does not need a prototype scope\nas in Spring or NO_SCOPE as in Guice because `python-inject` does not steal your class \nconstructors. Create instances the way you like and then inject dependencies into them.\n\nOther scopes such as a request scope or a session scope are fragile, introduce high coupling,\nand are difficult to test. In `python-inject` write custom providers which can be thread-local, \nrequest-local, etc.\n\nFor example, a thread-local current user provider:\n\n```python\nimport inject\nimport threading\n\n# Given a user class.\nclass User(object):\n    pass\n\n# Create a thread-local current user storage.\n_LOCAL = threading.local()\n\ndef get_current_user():\n    return getattr(_LOCAL, 'user', None)\n\ndef set_current_user(user):\n    _LOCAL.user = user\n\n# Bind User to a custom provider.\ninject.configure(lambda binder: binder.bind_to_provider(User, get_current_user))\n\n# Inject the current user.\n@inject.params(user=User)\ndef foo(user):\n    pass\n```\n\n## Links\n* Project: https://github.com/ivankorobkov/python-inject\n\n## License\nApache License 2.0\n\n## Contributors\n* Ivan Korobkov [@ivankorobkov](https://github.com/ivankorobkov)\n* Jaime Wyant [@jaimewyant](https://github.com/jaimewyant)\n* Sebastian Buczyński [@Enforcer](https://github.com/Enforcer)\n* Oleksandr Fedorov [@Fedorof](https://github.com/Fedorof)\n* cselvaraj [@cselvaraj](https://github.com/cselvaraj)\n* 陆雨晴 [@SixExtreme](https://github.com/SixExtreme)\n* Andrew William Borba [@andrewborba10](https://github.com/andrewborba10)\n* jdmeyer3 [@jdmeyer3](https://github.com/jdmeyer3)\n* Alex Grover [@ajgrover](https://github.com/ajgrover)\n* Harro van der Kroft [@wisepotato](https://github.com/wisepotato)\n* Samiur Rahman [@samiur](https://github.com/samiur)\n* 45deg [@45deg](https://github.com/45deg)\n* Alexander Nicholas Costas [@ancostas](https://github.com/ancostas)\n* Dmitry Balabka [@dbalabka](https://github.com/dbalabka)\n* Dima Burmistrov [@pyctrl](https://github.com/pyctrl)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivankorobkov%2Fpython-inject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fivankorobkov%2Fpython-inject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fivankorobkov%2Fpython-inject/lists"}