Skip to main content

A minimal IOC container inspired by Spring

Project description

PyPI version Build Status codecov

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:

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 for int, 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gean-0.0.11.tar.gz (8.3 kB view hashes)

Uploaded Source

Built Distribution

gean-0.0.11-py3-none-any.whl (7.7 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page