Dep man is a dependency manager library with dependency injection implementation and future annotations support for avoiding circular imports.
Project description
Dep man is a dependency manager library with dependency injection implementation and future annotations support for avoiding circular imports.
Installation
Install using pip install dep-man-pydi or uv add dep-man-pydi
Features
- ContextVar based injection
- Annotation like providers injection
- String annotations support
- ForwardRef annotations support
- Future annotations support
- Runtime annotations support
- Scopes support
- Custom providers scopes
- Interface based injection from different scopes
- Multiple scopes context
- Including other scopes external providers
- Context manager injection
- Sync manager support
- Async manager support
- Nested context managers usage
- Global context with optional immediate injection
- Classes support
- Class instances injection
- Class providers inheritance
- Nested providers in classes attrs
- Interface based class instance injection
- Injection via context manager
- Injection via global context
- Mark as injectable via decorating
- Sync function result attrs injection
- Async function result attrs injection
- Functions support
- Sync function result injection
- Async function result injection
- Nested providers in function args
- Protocol based function result injection
- Injection via context manager
- Injection via global context
- Injection via decorating
- Singleton support
- App level singletons (including any functions results)
- Global context singleton support
- Current context singleton support
- Dependency manager
- Multi DI managers support
- Custom DI managers support
- DI manager custom Scope type
- DI manager custom Injector type
- Integrations
- Django middleware
- Starlet middleware (can use with FastAPI)
Examples
# docs/examples/home/minimal/usage.py
from collections.abc import Awaitable
from dep_man import BIND, Depend, FDepend, dm
# declare sync function
def foo() -> str:
return "foo_value"
# declare async function
async def async_foo() -> str:
return "async_foo_value"
# declare function with dependence on foo
def bar(foo_value: FDepend[str, foo] = BIND) -> tuple[str, str]:
# also you can use Depend and FDepend with function or classes
# which also hame save annotations their values will be created
# from context or scope providers
return "bar_value", foo_value
# declare interface
class IFoo:
foo: bool
var: int
# declare class with dependence on foo and bar
class Foo(IFoo):
# in this case all fields with "Depend" of "FDepend" annotations
# will be replaced with descriptor for getting value from context
foo: FDepend[bool, foo]
bar: FDepend[int, bar]
# also you can use Depend and FDepend with function or classes
# which also hame save annotations their values will be created
# from context or scope providers
# inheritance providers is also supported
# declare function for providing singleton result
def singleton(arg: Depend[Foo] = BIND) -> Foo:
return arg
# I recommend creating a new scopes and call provide methods
# in the "dependencies.py" file in the roots of your modules or applications
# as scope name you can use Enum or str
scope = dm.add_scope("scope")
# provide functions and classes
scope.provide(foo)
scope.provide(async_foo)
scope.provide(bar)
# provide Foo with interface, you can use Depend[Foo] or Depend[IFoo] for getting Foo instance
scope.provide(Foo, interface=IFoo)
# singleton result function
# you can also provide object in certain scope using dm method
dm.provide(singleton, scope="scope", singleton=True)
# declare class with interface for other scope
class OtherFoo(IFoo):
foo = False
bar = -1
# create other scope
other_scope = dm.add_scope("other_scope")
# provide class in other scope with same interface
other_scope.provide(OtherFoo, interface=IFoo)
# next you need specify modules for loading
# if you have next structure
"""
...
├── app
└ ├── bar
│ ├── ...
│ ├── dependencies.py
│ ├── __init__.py
└── foo
├── ...
├── dependencies.py
├── __init__.py
"""
# you need make next load call
dm.load("app.bar", "app.foo")
# you can also specify file_name via load arg file_name
dm.load("app.bar", "app.foo", file_name="your_file")
# for django you need call this in ready method of you AppConfig
dm.load()
# at the beginning of the request you need call dm.init()
# if you use starlette, fastapy or django you can use middleware
from dep_man import get_django_middleware, get_starlette_middleware
# this method you need call for every request in middleware
dm.init()
# you can use globalize=True for add providers from all scopes globally to dm context
dm.init(globalize=True)
# or add to global context only certain scopes
dm.init(globalize=("notifications", "settings"))
# if you use context of run dm.init(globalize=True)
# you can create instance or call functions for any provider
# without context manager usage
foo_instance = Foo() # <__main__.Foo object at ...>
foo_instance.foo # 'foo_value'
foo_instance.bar # ('bar_value', 'foo_value')
# singleton function result
singleton() is singleton() # True
# If you want to inject dependencies into a class that was not provided,
# use need decorate this class with dm.inject as decorator
@dm.inject
class Injectable:
# in this case all fields with "Depend" of "FDepend" annotations
# will be replaced with descriptor for getting value from context
foo: Depend[Foo]
foo_from_interface: Depend[IFoo]
# usage example via context manager
with dm.inject("scope"):
# create instance of inject decorated class
instance = Injectable()
# Foo instance was created ones and set to instance.__dict__
instance.foo # <__main__.Foo object at ...>
instance.foo_from_interface # <__main__.Foo object at ...>
# foo call ones for getting result and set to instance.__dict__
instance.foo.foo # foo_value
# bar call ones for getting result and set to instance.__dict__
# inside the bar call foo was called once.
instance.foo.bar # ('bar_value', 'foo_value')
# you can also use nested context managers
with dm.inject("scope"):
instance = Injectable()
# In this context we will get the provider instance with interface=IFoo from the scope
isinstance(instance.foo_from_interface, Foo) # True
with dm.inject("other_scope"):
instance = Injectable()
# In this context we will get the provider instance with interface=IFoo from the other_scope
isinstance(instance.foo_from_interface, OtherFoo) # True
# usage example via function decoration
# here you can specify scopes or inject all if not specify
@dm.inject("scope")
def injectable(common: bool, arg: Depend[Foo] = BIND):
# in this case injectable __code__ will be replaced passing
# providers via signature.parameters defaults values from context
return common, arg.foo, arg.bar
# function call will be run with dm.inject("scope") context
injectable(True) # (True, 'foo_value', ('bar_value', 'foo_value'))
# async support logic of the injector's operation is similar
@dm.inject
class Foo:
# you can add async function result to you instances attrs
async_attr: FDepend[Awaitable[bool], async_foo]
# you can use async variant of context manager
async with dm.inject("scope"):
async with dm.inject("other_scope"):
# you can get async function result from provider
await Foo().async_attr # async_foo_value
# you can use inject decorator on async function
@dm.inject("scope")
async def async_injectable(common: bool, arg: FDepend[Awaitable[bool], async_foo] = BIND):
return common, await arg
await async_injectable(common=True) # (True, 'async_foo_value')
Changelog
v0.2.0
- Add permanent singletons supporting
v0.1.3
- Fix root all
v0.1.2
- Add core logic
- Add tests
- Add minimal docs
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dep_man_pydi-0.2.0.tar.gz.
File metadata
- Download URL: dep_man_pydi-0.2.0.tar.gz
- Upload date:
- Size: 34.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
23d8a8cb9fad9e6e2fd06680953c93989bbc1151560927b39f836db09acf4fae
|
|
| MD5 |
9bc4598420d63e1924364099f817b159
|
|
| BLAKE2b-256 |
355bed9da0450b0063f9981c6fc9d58da11453a9369021cdab13c2c02c49c3a4
|
Provenance
The following attestation bundles were made for dep_man_pydi-0.2.0.tar.gz:
Publisher:
ci.yml on extralait-web/dep-man
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dep_man_pydi-0.2.0.tar.gz -
Subject digest:
23d8a8cb9fad9e6e2fd06680953c93989bbc1151560927b39f836db09acf4fae - Sigstore transparency entry: 892233079
- Sigstore integration time:
-
Permalink:
extralait-web/dep-man@498ec66547d14a162cb779e278c595b74ed44891 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/extralait-web
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@498ec66547d14a162cb779e278c595b74ed44891 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dep_man_pydi-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dep_man_pydi-0.2.0-py3-none-any.whl
- Upload date:
- Size: 30.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17f1ac3d7f9db283bcbfb1bf6e6508ca474227e2fefb9fd7514f687ca9d1ae49
|
|
| MD5 |
af18e7eead30ebaaefe754e001bc55bf
|
|
| BLAKE2b-256 |
d4cfdc78a94bc0247544ce2f96ff911a5cf8c45f56d5f55ef3cbfdf741f48d80
|
Provenance
The following attestation bundles were made for dep_man_pydi-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on extralait-web/dep-man
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dep_man_pydi-0.2.0-py3-none-any.whl -
Subject digest:
17f1ac3d7f9db283bcbfb1bf6e6508ca474227e2fefb9fd7514f687ca9d1ae49 - Sigstore transparency entry: 892233125
- Sigstore integration time:
-
Permalink:
extralait-web/dep-man@498ec66547d14a162cb779e278c595b74ed44891 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/extralait-web
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@498ec66547d14a162cb779e278c595b74ed44891 -
Trigger Event:
push
-
Statement type: