Skip to main content

Dependency injection library for Python.

Project description

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

# 1. Define your components


class A:
    def __str__(self):
        return "A"


class B:
    def __init__(self, a: A):
        self.a = a


# 2. Create a cargo container

container = cargo.containers.Standard()


# 3. Register your components

container[A] = A
container[B] = B


# 4. Use cargo to initialize your components

b = container[B]


# 5. Use your components

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

# Prints: Hello <__main__.A object at 0x7f863b0fd450> and <__main__.B object at 0x7f863b09b810>
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()

# Registers a factory
container[DatabaseClient] = database_client_factory

# Registers a value
container[DatabaseURL] = "mysql://user:password@host:3306/db"

db_client = container[DatabaseClient]

print(db_client)  # Prints: <__main__.MysqlClient object at 0x7f681975b390>

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

# Prints:
# Start resolving <class '__main__.B'>
# Start resolving <class '__main__.A'>
# End resolving <class '__main__.A'>
# End resolving <class '__main__.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
# Note: B has not been registered

# Raises cargo.exceptions.DependencyNotFound: <class '__main__.B'>
container[A]

Circular dependency

Cargo raises a CircularDependency exception with the dependency cycle when a circular dependency is detected.

examples/circular_dependency.py:

...

# Dependencies:
#   - A depends on B
#   - B depends on C
#   - C depends on D
#   - D depends on B
#
# Circular dependency cycle is: B -> C -> D -> B

...

container = cargo.containers.Standard()
container[A] = A
container[B] = B
container[C] = C
container[D] = D

# Raises cargo.exceptions.CircularDependency:
#   [<class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.B'>]
container[A]

Contributors

License

Cargo is licensed under the terms of the MIT license.

Website

https://github.com/larose/cargo

Project details


Release history Release notifications | RSS feed

This version

0.3

Download files

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

Source Distribution

cargo-0.3.tar.gz (7.4 kB view hashes)

Uploaded Source

Built Distribution

cargo-0.3-py3-none-any.whl (7.2 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