A minimal IOC container inspired by Spring
Project description
gean
A minimal IOC container inspired by Spring.
Install
python3 -m pip install gean
Requirements
gean
, like Spring, relies on types and signatures to build and resolve the dependency graph.
Required language features:
- PEP 484 - Type Hints (Python 3.5+)
- PEP 526 - Syntax for Variable Annotations (Python 3.6+)
Features
Type hierarchies
A dependency of a given type X
is exposed not only as X
but also all of its super types, including generic interfaces. Variance is supported on generic types.
Regular inheritance
from abc import ABC
from gean import Container
# Works with or without ABC
class Worker(ABC): pass
class WorkerImpl(Worker): pass
container = Container()
container.register_class(WorkerImpl)
# All of these return the same instance
c1 = container.resolve(WorkerImpl)
c2 = container.resolve(Worker)
assert c1 is c2
assert isinstance(c1, WorkerImpl)
Covariance
from gean import Container
from typing import Generic, TypeVar
_Tco = TypeVar('_Tco', covariant=True)
class Person: pass
class Student(Person): pass
class Factory(Generic[_Tco]): pass
class StudentFactory(Factory[Student]): pass
container = Container()
container.register_class(StudentFactory)
# All of these return the same instance
c1 = container.resolve(StudentFactory)
c2 = container.resolve(Factory[Student])
c3 = container.resolve(Factory[Person])
assert c1 is c2 is c3
assert isinstance(c1, StudentFactory)
Contravariance
from gean import Container
from typing import Generic, TypeVar
_Tcontra = TypeVar('_Tcontra', contravariant=True)
class Person: pass
class Student(Person): pass
class Validator(Generic[_Tcontra]): pass
class PersonValidator(Validator[Person]): pass
container = Container()
container.register_class(PersonValidator)
# All of these return the same instance
c1 = container.resolve(PersonValidator)
c2 = container.resolve(Validator[Student])
c3 = container.resolve(Validator[Person])
assert c1 is c2 is c3
assert isinstance(c1, PersonValidator)
Caching
All dependencies are cached as they are constructed.
from gean import Container
class A: pass
container = Container()
container.register_class(A)
a1 = container.resolve(A)
a2 = container.resolve(A)
assert a1 is a2
Autowiring
Dependencies can be autowired if a class does not declare an explicit constructor.
Field names can disambiguate same-type dependencies.
from gean import Container
class Subject:
def work(self):
print('working')
class Manager:
subject: Subject # will be autowired
def run(self):
self.subject.work()
container = Container()
# Order of registration does not matter
container.register_class(Manager)
container.register_class(Subject)
# This prints 'working'
container.resolve(Manager).run()
Constructor wiring
If a class defines an explicit constructor, dependencies will be passed as arguments.
Parameter names can disambiguate same-type dependencies.
from gean import Container
class Subject:
def work(self):
print('working')
class Manager:
def __init__(self, subject: Subject):
self.subject = subject
def run(self):
self.subject.work()
container = Container()
# Order of registration does not matter
container.register_class(Manager)
container.register_class(Subject)
# This prints 'working'
container.resolve(Manager).run()
Modules
A module is a class whose name ends in Module
.
A module may use @includes
to declaratively register other classes or modules.
A module may use public methods to create dependencies programmatically.
from gean import Container, includes
class PingService:
def ping(self, addr): ...
class DNSService:
def resolve(self, name): ...
@includes(
DNSService,
PingService,
)
class NetworkModule: pass
class AppConfig: pass
class Application:
config: AppConfig
dns_service: DNSService
ping_service: PingService
def run(self):
print(self.config)
self.ping_service.ping(self.dns_service.resolve('garciat.com'))
def load_configuration() -> AppConfig: ...
@includes(
NetworkModule,
Application,
)
class ApplicationModule:
# Create config programmatically
def config(self) -> AppConfig:
return load_configuration()
container = Container()
# No other dependencies need to be declared manually
# Because the modules do so declaratively
container.register_module(ApplicationModule)
container.resolve(Application).run()
Singletons
Dependencies can be explicitly named so that disambiguation is possible.
from gean import Container
container = Container()
# Both dependencies are of type `str`
container.register_instance('/tmp', name='tmp_dir')
container.register_instance('/home/garciat', name='user_dir')
# Disambiguate with name
container.resolve(str, name='tmp_dir')
Alternatives
As of June 1 2020, this is a non-exhaustive list of alternative solutions that also leverage Type Hints.
injector
- Does not support hierarchy with generic interfaces
from typing import Generic, TypeVar
from injector import Injector, Module, inject, provider, singleton
_T = TypeVar('_T')
class A(Generic[_T]): pass
class B(A[int]): pass
class C:
@inject
def __init__(self, a: A[int]):
self.a = a
class MyModule(Module):
@singleton
@provider
def provide_b(self) -> B: # works if return type is A[int] explicitly
return B()
i = Injector([MyModule])
# injector.UnknownProvider: couldn't determine provider for __main__.A[int] to None
i.get(C)
bobthemighty/punq
- Does not support type hierarchies
jbasko/auto-init
- Performs default initialization. E.g.
0
forint
,None
for objects - Does not support generic interfaces
asyncee/wint
- Global state
History
gean
started off as a gist I created to show @alexpizarroj how my team leverages Spring in our projects.
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
File details
Details for the file gean-0.0.11.tar.gz
.
File metadata
- Download URL: gean-0.0.11.tar.gz
- Upload date:
- Size: 8.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/47.1.1 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 8740307a02471d7e2d00a443b3ca1054012518e32722320b3340374a580f41d0 |
|
MD5 | 5a0d218a3ea57a1544058e8b3f16fbd7 |
|
BLAKE2b-256 | 451c0a0754dbf3efae94993fd5480abbe78a409e8b76cc746d31201b8b830dcf |
File details
Details for the file gean-0.0.11-py3-none-any.whl
.
File metadata
- Download URL: gean-0.0.11-py3-none-any.whl
- Upload date:
- Size: 7.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/47.1.1 requests-toolbelt/0.9.1 tqdm/4.46.0 CPython/3.7.7
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 31723c9ace9ee9ea37082366d2110d98b369dbd6ce88453b034a8284ca7eed09 |
|
MD5 | 771160044c1293c3aaf46c21a506f8c1 |
|
BLAKE2b-256 | ea7694d4ffc5041f48e4112cd69d9660ef43099afc0e323772aa7eda6f239c8e |