Science Score: 26.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
○CITATION.cff file
-
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Committers with academic emails
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (10.9%) to scientific vocabulary
Repository
Python dependency injection
Basic Info
Statistics
- Stars: 731
- Watchers: 17
- Forks: 84
- Open Issues: 29
- Releases: 0
Metadata Files
README.md
python-inject 
Dependency injection the python way, the good way.
Key features
- Fast.
- Thread-safe.
- Simple to use.
- Does not steal class constructors.
- Does not try to manage your application object graph.
- Transparently integrates into tests.
- Autoparams leveraging type annotations.
- Supports type hinting in Python 3.5+.
- Supports Python 3.9+ (
v5.*), 3.5-3.8 (v4.*) and Python 2.7–3.5 (v3.*). - Supports context managers.
Python Support
| Python | Inject Version | |---------|----------------| | 3.9+ | 5.0+ | | 3.6-3.8 | 4.1+, < 5.0 | | 3.5 | 4.0 | | < 3.5 | 3.* |
Installation
Use pip to install the lastest version:
bash
pip install inject
Autoparams example
@inject.autoparams returns a decorator which automatically injects arguments into a function
that uses type annotations. This is supported only in Python >= 3.5.
python
@inject.autoparams
def refresh_cache(cache: RedisCache, db: DbInterface):
pass
There is an option to specify which arguments we want to inject without attempts of injecting everything:
python
@inject.autoparams('cache', 'db')
def sign_up(name, email, cache: RedisCache, db: DbInterface):
pass
It is also acceptable to use explicit curly braces notation (@inject.autoparams())
for non-parameterized decorations — it will be treated the same as @inject.autoparams.
Step-by-step example
```python
Import the inject module.
import inject
inject.instance requests dependencies from the injector.
def foo(bar): cache = inject.instance(Cache) cache.save('bar', bar)
inject.params injects dependencies as keyword arguments or positional argument.
Also you can use @inject.autoparams in Python 3.5, see the example above.
@inject.params(cache=Cache, user=CurrentUser) def baz(foo, cache=None, user=None): cache.save('foo', foo, user)
this can be called in different ways:
with injected arguments
baz('foo')
with positional arguments
baz('foo', my_cache)
with keyword arguments
baz('foo', mycache, user=currentuser)
inject.param is deprecated, use inject.params instead.
@inject.param('cache', Cache) def bar(foo, cache=None): cache.save('foo', foo)
inject.attr creates properties (descriptors) which request dependencies on access.
class User(object): cache = inject.attr(Cache)
def __init__(self, id):
self.id = id
def save(self):
self.cache.save('users', self)
@classmethod
def load(cls, id):
return cls.cache.load('users', id)
Create an optional configuration.
def my_config(binder): binder.bind(Cache, RedisCache('localhost:1234'))
Configure a shared injector.
inject.configure(my_config)
Instantiate User as a normal class. Its cache dependency is injected when accessed.
user = User(10) user.save()
Call the functions, the dependencies are automatically injected.
foo('Hello') bar('world') ```
Context managers
Binding a class to an instance of a context manager (through bind or bind_to_constructor)
or to a function decorated as a context manager leads to the context manager to be used as is,
not via with statement.
```python @contextlib.contextmanager def getfilesync(): obj = MockFile() yield obj obj.destroy()
@contextlib.asynccontextmanager async def getconnasync(): obj = MockConnection() yield obj obj.destroy()
def config(binder): binder.bindtoprovider(MockFile, getfilesync) binder.bind(int, 100) binder.bindtoprovider(str, lambda: "Hello") binder.bindtoprovider(MockConnection, getconnsync)
inject.configure(config)
@inject.autoparams() def example(conn: MockConnection, file: MockFile): # Connection and file will be automatically destroyed on exit. pass ```
Usage with Django
Django can load some modules multiple times which can lead to
InjectorException: Injector is already configured. You can use configure(once=True) which
is guaranteed to run only once when the injector is absent:
python
import inject
inject.configure(my_config, once=True)
Testing
In tests use inject.configure(callable, clear=True) to create a new injector on setup,
and optionally inject.clear() to clean up on tear down:
```python
class MyTest(unittest.TestCase):
def setUp(self):
inject.configure(lambda binder: binder
.bind(Cache, MockCache()) \
.bind(Validator, TestValidator()),
clear=True)
def tearDown(self):
inject.clear()
```
Composable configurations
You can reuse configurations and override already registered dependencies to fit the needs in different environments or specific tests. ```python def base_config(binder): # ... more dependencies registered here binder.bind(Validator, RealValidator()) binder.bind(Cache, RedisCache('localhost:1234'))
def tests_config(binder):
# reuse existing configuration
binder.install(base_config)
# override only certain dependencies
binder.bind(Validator, TestValidator())
binder.bind(Cache, MockCache())
inject.configure(tests_config, allow_override=True, clear=True)
```
Thread-safety
After configuration the injector is thread-safe and can be safely reused by multiple threads.
Binding types
Instance bindings always return the same instance:
python
redis = RedisCache(address='localhost:1234')
def config(binder):
binder.bind(Cache, redis)
Constructor bindings create a singleton on injection:
python
def config(binder):
# Creates a redis cache singleton on first injection.
binder.bind_to_constructor(Cache, lambda: RedisCache(address='localhost:1234'))
Provider bindings call the provider on injection:
```python def getmythreadlocalcache(): pass
def config(binder): # Executes the provider on each injection. binder.bindtoprovider(Cache, getmythreadlocalcache) ```
Runtime bindings automatically create singletons on injection, require no configuration.
For example, only the Config class binding is present, other bindings are runtime:
```python class Config(object): pass
class Cache(object): config = inject.attr(Config)
class Db(object): config = inject.attr(Config)
class User(object): cache = inject.attr(Cache) db = inject.attr(Db)
@classmethod
def load(cls, user_id):
return cls.cache.load('users', user_id) or cls.db.load('users', user_id)
inject.configure(lambda binder: binder.bind(Config, loadconfigfile())) user = User.load(10) ```
Disabling runtime binding
Sometimes runtime binding leads to unexpected behaviour. Say if you forget
to bind an instance to a class, inject will try to implicitly instantiate it.
If an instance is unintentionally created with default arguments it may lead to
hard-to-debug bugs. To disable runtime binding and make sure that only
explicitly bound instances are injected, pass bind_in_runtime=False to inject.configure.
In this case inject immediately raises InjectorException when the code
tries to get an unbound instance.
Keys
It is possible to use any hashable object as a binding key. For example:
```python import inject
inject.configure(lambda binder: \ binder.bind('host', 'localhost') \ binder.bind('port', 1234)) ```
Why no scopes?
I've used Guice and Spring in Java for a lot of years, and I don't like their scopes.
python-inject by default creates objects as singletons. It does not need a prototype scope
as in Spring or NO_SCOPE as in Guice because python-inject does not steal your class
constructors. Create instances the way you like and then inject dependencies into them.
Other scopes such as a request scope or a session scope are fragile, introduce high coupling,
and are difficult to test. In python-inject write custom providers which can be thread-local,
request-local, etc.
For example, a thread-local current user provider:
```python import inject import threading
Given a user class.
class User(object): pass
Create a thread-local current user storage.
_LOCAL = threading.local()
def getcurrentuser(): return getattr(_LOCAL, 'user', None)
def setcurrentuser(user): _LOCAL.user = user
Bind User to a custom provider.
inject.configure(lambda binder: binder.bindtoprovider(User, getcurrentuser))
Inject the current user.
@inject.params(user=User) def foo(user): pass ```
Links
- Project: https://github.com/ivankorobkov/python-inject
License
Apache License 2.0
Contributors
- Ivan Korobkov @ivankorobkov
- Jaime Wyant @jaimewyant
- Sebastian Buczyński @Enforcer
- Oleksandr Fedorov @Fedorof
- cselvaraj @cselvaraj
- 陆雨晴 @SixExtreme
- Andrew William Borba @andrewborba10
- jdmeyer3 @jdmeyer3
- Alex Grover @ajgrover
- Harro van der Kroft @wisepotato
- Samiur Rahman @samiur
- 45deg @45deg
- Alexander Nicholas Costas @ancostas
- Dmitry Balabka @dbalabka
- Dima Burmistrov @pyctrl
Owner
- Name: Ivan Korobkov
- Login: ivankorobkov
- Kind: user
- Location: Bali, Indonesia
- Repositories: 3
- Profile: https://github.com/ivankorobkov
GitHub Events
Total
- Create event: 5
- Commit comment event: 1
- Issues event: 6
- Watch event: 42
- Delete event: 1
- Member event: 1
- Issue comment event: 29
- Push event: 15
- Pull request review comment event: 13
- Pull request review event: 14
- Pull request event: 25
- Fork event: 4
Last Year
- Create event: 5
- Commit comment event: 1
- Issues event: 6
- Watch event: 42
- Delete event: 1
- Member event: 1
- Issue comment event: 29
- Push event: 15
- Pull request review comment event: 13
- Pull request review event: 14
- Pull request event: 25
- Fork event: 4
Committers
Last synced: over 2 years ago
Top Committers
| Name | Commits | |
|---|---|---|
| Ivan Korobkov | i****v@g****m | 229 |
| Sebastian Buczyński | n****a@g****m | 9 |
| Johan Westin | j****n@g****m | 7 |
| Alexander Costas | a****s@m****m | 7 |
| Dmitry Balabka | d****a@g****m | 4 |
| Alexander Panko | g****d@p****u | 3 |
| SixExtreme | 9****9@q****m | 3 |
| Ivan Korobkov | i****v@i****u | 3 |
| EspenAlbert | a****n@g****m | 3 |
| Harro van der Kroft | w****o | 2 |
| Oleksandr Fedorov | a****f@g****m | 2 |
| jmeyer | j****r@c****m | 2 |
| Michael Scharf (Github) | g****b@s****r | 2 |
| Andrew William Borba | a****a@f****m | 2 |
| Marcelo Avancini | m****n@g****m | 1 |
| Michael Peick | m****k@g****t | 1 |
| Didier Roche | d****s@u****m | 1 |
| Chris Selvaraj | c****j@g****m | 1 |
| Nick Harris | n****s@c****m | 1 |
| Samiur Rahman | s****r@u****m | 1 |
| Nikolay Tsvetanov | n****v@g****m | 1 |
| Viktor Kerkez | a****a@g****m | 1 |
| jaime | p****y@g****m | 1 |
| Alex Grover | h****o@a****e | 1 |
| hf-kklein | k****n@h****e | 1 |
| Yuya Unno | u****o@g****m | 1 |
| Karthikeyan Singaravelan | t****i@g****m | 1 |
| 45deg | 4****g | 1 |
Committer Domains (Top 20 + Academic)
Issues and Pull Requests
Last synced: 10 months ago
All Time
- Total issues: 69
- Total pull requests: 56
- Average time to close issues: 3 months
- Average time to close pull requests: 3 months
- Total issue authors: 57
- Total pull request authors: 36
- Average comments per issue: 2.97
- Average comments per pull request: 1.54
- Merged pull requests: 42
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 6
- Pull requests: 13
- Average time to close issues: 2 days
- Average time to close pull requests: 1 day
- Issue authors: 6
- Pull request authors: 3
- Average comments per issue: 2.33
- Average comments per pull request: 0.31
- Merged pull requests: 6
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- ivankorobkov (5)
- andrewborba10 (3)
- dbalabka (3)
- shtlrs (2)
- nikordaris (2)
- ancostas (2)
- 1995chen (2)
- Gr1N (1)
- dimma837 (1)
- suve (1)
- scharf (1)
- krcourville (1)
- alex-grover (1)
- mayesca (1)
- dineshtrivedi (1)
Pull Request Authors
- pyctrl (10)
- dbalabka (4)
- andrewborba10 (3)
- Enforcer (3)
- markhobson (2)
- Sikerdebaard (2)
- wushengjianzong (2)
- fhdufhdu (2)
- Sukonnik-Illia (2)
- EspenAlbert (2)
- PerchunPak (2)
- juancerezo (2)
- panki (2)
- unnonouno (1)
- fzyukio (1)
Top Labels
Issue Labels
Pull Request Labels
Packages
- Total packages: 2
-
Total downloads:
- pypi 1,466,883 last-month
- Total docker downloads: 90
-
Total dependent packages: 16
(may contain duplicates) -
Total dependent repositories: 93
(may contain duplicates) - Total versions: 29
- Total maintainers: 1
pypi.org: inject
Python dependency injection framework.
- Homepage: https://github.com/ivankorobkov/python-inject
- Documentation: https://inject.readthedocs.io/
- License: Apache Software License
-
Latest release: 5.3.0
published about 1 year ago
Rankings
Maintainers (1)
conda-forge.org: inject
- Homepage: https://github.com/ivankorobkov/python-inject
- License: Apache-2.0
-
Latest release: 4.3.1
published about 5 years ago