Cargo
Cargo is a dependency injection library for Python.
Cargo is simple to use, typed,
flexible, extensible and
easy to debug.
Getting started
Step 1: Install cargo
With pip:
$ pip install cargo
With pipenv:
$ pipenv install cargo
With poetry:
$ poetry add cargo
Step 2: Use cargo
examples/intro.py:
import cargo
class A:
def __str__(self):
return "A"
class B:
def __init__(self, a: A):
self.a = a
container = cargo.containers.Standard()
container[A] = A
container[B] = B
b = container[B]
print(b.a)
Features
All the examples are located in the examples directory.
Cargo is typed
Cargo uses the argument types to inject the dependencies; not their
names.
examples/hello_dependencies.py:
import cargo
class A:
pass
class B:
pass
class Hello:
def __init__(self, foo: A, bar: B):
print(f"Hello {foo} and {bar}")
container = cargo.containers.Standard()
container[A] = A
container[B] = B
container[Hello] = Hello
container[Hello]
Cargo is flexible
Functions and methods can be used as factories; and objects as values.
examples/factory_and_value.py:
...
DatabaseURL = typing.NewType("DatabaseURL", str)
def database_client_factory(db_url: DatabaseURL) -> DatabaseClient:
if db_url.startswith("mysql://"):
return MysqlClient(db_url)
if db_url.startswith("postgres://"):
return PostgresClient(db_url)
raise Exception(f"Invalid database url: {db_url}")
container = cargo.containers.Standard()
container[DatabaseClient] = database_client_factory
container[DatabaseURL] = "mysql://user:password@host:3306/db"
db_client = container[DatabaseClient]
print(db_client)
Cargo is extensible
Cargo composes middlewares to create containers. The
Standard
container is just a stack of
opiniated middlewares. You can create your own types of containers
with the middlewares you want, or even create
your own middlewares.
examples/my_container.py:
import cargo
class LoggerMiddleware(cargo.types.Middleware):
def execute(
self,
dependency_type: cargo.types.DependencyType,
next_middleware: cargo.types.NextMiddleware,
):
print(f"Start resolving {dependency_type}")
dependency_value = next_middleware()
print(f"End resolving {dependency_type}")
return dependency_value
middleware_factories = [
LoggerMiddleware,
cargo.middlewares.CircularDependencyCircuitBreaker,
cargo.middlewares.Singleton,
]
container = cargo.containers.create(middleware_factories)
class A:
pass
class B:
def __init__(self, a: A):
pass
container[A] = A
container[B] = B
container[B]
Cargo is easy to debug
Dependency not found
Cargo raises a DependencyNotFound
exception
with the missing dependency type when a dependency is not found.
examples/dependency_not_found.py:
...
class A:
def __init__(self, b: B):
pass
class B:
pass
container = cargo.containers.Standard()
container[A] = A
container[A]
Circular dependency
Cargo raises a CircularDependency
exception
with the dependency cycle when a circular dependency is detected.
examples/circular_dependency.py:
...
...
container = cargo.containers.Standard()
container[A] = A
container[B] = B
container[C] = C
container[D] = D
container[A]
Contributors
License
Cargo is licensed under the terms of the MIT license.
Website
https://github.com/larose/cargo