Skip to main content

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.

Metadata

Release files for gean 0.0.11

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gean 0.0.11
File Size Uploaded
gean-0.0.11.tar.gz 8.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gean 0.0.11
File Interpreter ABI Platform
gean-0.0.11-py3-none-any.whl Python 3 none any Details

Total release size: 16.0 kB

Release files / gean-0.0.11.tar.gz

Download URL gean-0.0.11.tar.gz
Size 8.3 kB
Tags Source
SHA-256 checksum
How to use checksums
8740307a02471d7e2d00a443b3ca1054012518e32722320b3340374a580f41d0
BLAKE2b-256 checksum
How to use checksums
451c0a0754dbf3efae94993fd5480abbe78a409e8b76cc746d31201b8b830dcf
Upload date
Uploaded using Trusted Publishing?
What is 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

Release files / gean-0.0.11-py3-none-any.whl

Download URL gean-0.0.11-py3-none-any.whl
Size 7.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
31723c9ace9ee9ea37082366d2110d98b369dbd6ce88453b034a8284ca7eed09
BLAKE2b-256 checksum
How to use checksums
ea7694d4ffc5041f48e4112cd69d9660ef43099afc0e323772aa7eda6f239c8e
Upload date
Uploaded using Trusted Publishing?
What is 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

Release history Release notifications | RSS feed

This release

0.0.11 This release

2 release files

0.0.9

2 release files

0.0.8

3 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page