Skip to content

Library design and methods

Roberto Prevato edited this page Dec 19, 2022 · 11 revisions

rodi works by inspecting __init__ methods once at runtime, to generate functions that return instances of desired types. Validation steps, for example to detect circular dependencies or missing services, are done when building these functions, so additional validation is not needed when activating services.

For this reason, services are first registered inside an instance of Container class, which implements a method build_provider() that returns an instance of Services. The service provider is then used to obtain desired services by type or name. Inspection and validation steps are done only when creating an instance of service provider.

Container methods

add_instance(instance, optional declared_type)

Registers an exact instance (singleton), optionally specifying a declared type. If the type is not specified, the type of the instance is used.

add_singleton(base_type, concrete_type)

Registers a singleton by base and concrete type, its dependencies are resolved dynamically when the provider is built.

add_transient(base_type, concrete_type)

Registers a service by base and concrete type. Every time the service is required, a new instance is created. Dependencies are resolved dynamically when the provider is built.

add_scoped(base_type, concrete_type)

Registers a service by base and concrete type. Within the context of a single call to obtain a service (provider.get), only a single instance is created. Dependencies are resolved dynamically when the provider is built.

add_exact_singleton(concrete_type)

Registers a singleton by concrete type, its dependencies are resolved dynamically when the provider is built.

add_exact_transient(concrete_type)

Registers a service by concrete type. Every time the service is required, a new instance is created. Dependencies are resolved dynamically when the provider is built.

add_exact_scoped(concrete_type)

Registers a service by concrete type. Within the context of a single call to obtain a service (provider.get), only a single instance is created. Dependencies are resolved dynamically when the provider is built.

add_singleton_by_factory(factory, optional concrete_type)

Registers a singleton by factory. The first time the singleton is required, the given factory function is used. Factories receive the instance of service provider in input. If the factory is decorated with type hints, it is not necessary to specify the service type.

def foo_factory(provider) -> Foo:
    return Foo()

services.add_singleton_by_factory(foo_factory)  # fine, because the type is obtained from function hint

def ufo_factory(provider):
    return Ufo()

services.add_singleton_by_factory(ufo_factory, Ufo)  # fine, because the type is specified

services.add_singleton_by_factory(ufo_factory)  # raises exception because the type must be specified

add_transient_by_factory(base_type, optional concrete_type)

Registers a transient service by factory. The given factory function is called whenever the service is instantiated.

add_scoped_by_factory(base_type, optional concrete_type)

Registers a scoped service by factory. The given factory function is called whenever the service is instantiated.

register(base_type, concrete_type, lifestyle)

Registers service by base, concrete types and life style. This method is used internally by other methods involving type resolution.

register_factory(factory, optional return_type)

Registers service by factory and optionally return type. This method is used internally by other methods involving factories.

__contains__(self, item)

Returns true if a given type or type name is configured in the service configuration, false otherwise.

services.add_transient(IFoo, MemoryFoo)

assert IFoo in services  # OK, because IFoo is configured

Services methods

get(type)

Returns a service of given type, by type name or class.

set(type, value)

Adds a singleton to the map of services. This method exists to make an instance of Services compatible with a regular dictionary.

__getitem__(self, item)

Returns a service of given type, by type name or class (dictionary notation).

__setitem__(self, item, value)

Adds a singleton to the map of services, with dictionary notation. This method exists to make an instance of Services compatible with a regular dictionary.

__contains__(self, item)

Returns true if a given type or type name is handled by the service provider, false otherwise.

services.add_transient(IFoo, MemoryFoo)

provider = services.build_provider()

assert IFoo in provider  # OK, because IFoo is configured