hexdi
Highly extensible Dependency Injection framework for humans
Project location: https://github.com/zibertscrem/hexdi
Installation
pip install hexdi
You should have python 3.5.* or higher
Usage
All of that usages you can find in examples directory
Quick usage reference
import hexdi
class SomeA:
def foo(self): pass
# mark that class as injectable with permanent lifetime for class SomeA
@hexdi.permanent(SomeA)
class SomeAimplementation(SomeA):
def foo(self):
return 42
# inject instance of SomeA as a first argument
@hexdi.inject(SomeA)
def test_injection(a: SomeA):
print('test_injection:', a.foo())
class ClassWithDependency:
# constructor injection
@hexdi.inject(SomeA)
def __init__(self, a: SomeA):
print('ClassWithDependency.__init__:', a.foo())
# after that we can use property like an instance of SomeA class
@property
@hexdi.dependency(SomeA)
def some_a(self) -> SomeA: pass
def foo(self):
print('ClassWithDependency.foo:', self.some_a.foo())
# method injection also works fine.
# Because injection members are passing after all transmitted positional arguments
@hexdi.inject(SomeA)
def foo_with_injection(self, a: SomeA):
print('ClassWithDependency.foo_with_injection:', a.foo())
if __name__ == '__main__':
# You don't need to provide any argument. DI container does it self
# There also should not be cycle dependencies due to lazy loading of any injections
test_injection() # prints: test_injection: 42
cwd = ClassWithDependency() # prints: ClassWithDependency.__init__: 42
cwd.foo() # prints: ClassWithDependency.foo: 42
cwd.foo_with_injection() # prints: ClassWithDependency.foo_with_injection: 42
Self-binding
import hexdi
@hexdi.permanent()
class SomeA:
def foo(self):
return 42
@hexdi.inject(SomeA)
def test(a):
print(a.foo())
if __name__ == '__main__':
test() # prints: 42
Multiple injection arguments
import hexdi
@hexdi.permanent()
class SomeA:
def foo(self):
return 42
@hexdi.permanent()
class SomeB:
def foo(self):
return 69
@hexdi.inject(SomeA, SomeB)
def test(a, b):
print(a.foo() + b.foo())
if __name__ == '__main__':
test() # prints: 111
Permanent lifetime and transient lifetime
import hexdi
@hexdi.permanent()
class SomeA:
NUMBER = 0
def __init__(self):
self.num = SomeA.NUMBER
SomeA.NUMBER += 1
def foo(self):
print(self.__class__.__name__, self.num)
@hexdi.transient()
class SomeB:
NUMBER = 0
def __init__(self):
self.num = SomeB.NUMBER
SomeB.NUMBER += 1
def foo(self):
print(self.__class__.__name__, self.num)
@hexdi.inject(SomeA)
def test_a(a):
a.foo()
@hexdi.inject(SomeB)
def test_b(b):
b.foo()
if __name__ == '__main__':
test_a() # prints: SomeA 0
test_a() # prints: SomeA 0
test_a() # prints: SomeA 0
test_b() # prints: SomeB 0
test_b() # prints: SomeB 1
test_b() # prints: SomeB 2
Usage of container. Demonstration of lazy injection
import hexdi
class SomeA:
def foo(self): pass
class SomeAImplementation(SomeA):
def foo(self):
return 42
@hexdi.permanent()
class SomeB:
def foo(self):
return 69
class SomeC:
def foo(self):
return 100500
@hexdi.inject(SomeC)
def test(c):
print(c.foo())
if __name__ == '__main__':
# getting of container
container = hexdi.get_root_container()
# binding SomeAImplementation on SomeA type with permanent lifetime
container.bind_type(SomeAImplementation, SomeA, hexdi.lifetime.PermanentLifeTimeManager)
instance = container.resolve(SomeA)
print(instance.foo()) # prints: 42
# resolve decorator-binded SomeB
instance = container.resolve(SomeB)
print(instance.foo()) # prints: 69
# bind SomeC on itself with permanent lifetime
container.bind_type(SomeC, SomeC, hexdi.lifetime.PermanentLifeTimeManager)
# we mark SomeC for injection above in test func,
# but all works fine, because it is lazy injection
test() # prints: 100500
Multifile project
If you have a project with separated base objects(to registration) and implementations(to injecting) there will be problematically to identify these implementations if you import it nowhere. For that situation, there is a class loading abstraction with a basic implementation that gets a list of specification objects with implementation modules. These specification object can be: - dot-separated module path as string: ‘pack1.pack2.module1’ - a function/lambda without params that returns a specification - a tuple that contains a function as a first argument and tuple of values as a second argument. Function should return a specification
import hexdi
from examples.multifile.interfaces import SomeA
loader = hexdi.get_loader([
'examples.multifile.implementations'
])
@hexdi.inject(SomeA)
def test(a: SomeA):
print(a.foo())
if __name__ == '__main__':
loader.load()
test() # prints: 42
You also able to use recursive module finder to find all local packages, site-packages, dist-packages modules that contains type registering. Use same rules as module loader has
import hexdi
from examples.multifile.interfaces import SomeA
# That finder will find that
finder = hexdi.get_finder(['examples.multifile-finder'])
loader = hexdi.get_loader(finder.find())
@hexdi.inject(SomeA)
def test(a: SomeA):
print(a.foo())
if __name__ == '__main__':
loader.load()
test() # prints: 69
Metadata
Release files for hexdi 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| hexdi-0.2.2.tar.gz | 9.6 kB | Without provenance | Details for hexdi-0.2.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| hexdi-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for hexdi-0.2.2-py3-none-any.whl |
Total release size: 25.0 kB
Release files / hexdi-0.2.2.tar.gz
| Download URL | hexdi-0.2.2.tar.gz |
|---|---|
| Size | 9.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3178d7cbfbd14da28c82b861623ec5a018216445c68642512a6b90cfb5601c21
|
|
BLAKE2b-256 checksum How to use checksums |
44f458fa75f343a3f97e55afb0f5cdf1c78c2c9c07e20431e51c227b8ae261d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.10.6
|
Release files / hexdi-0.2.2-py3-none-any.whl
| Download URL | hexdi-0.2.2-py3-none-any.whl |
|---|---|
| Size | 15.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d25f2bc5e95f4c87de737cc171150458e0d7c750b563dfc1cd75fab5c73584d8
|
|
BLAKE2b-256 checksum How to use checksums |
a74b8a4329a9a7c94b1cbd4684c975eeaa0532254059c23d43ff1c13de96a49d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.1 CPython/3.10.6
|
Security for hexdi 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.