Skip to main content

Dependency injection container

Project description

Independency

A modern, type-safe dependency injection container for Python 3.10+


Unlike many other Python DI containers, Independency operates in the local scope with no global state. It's inspired by punq with a similar API, enhanced with full support for generics, advanced typing, and comprehensive testing utilities.

Features

  • No Global State: Explicit container creation, no hidden dependencies
  • Type Safety: Full support for generics, type hints, and static type checking
  • Modern Python: Built for Python 3.10+ with modern typing features (PEP 585)
  • Flexible Registrations: Singleton and transient scopes, factory functions
  • Testing Support: Built-in TestContainer for easy dependency mocking
  • Build-time Validation: Dependency graph validation with cycle detection
  • String Keys: Support for string-based dependency registration
  • Self-injection: Container can be injected as a dependency

Installation

pip install independency

Quick Start

import independency

builder = independency.ContainerBuilder()
# register application dependencies
container = builder.build()
obj: SomeRegisteredClass = container.resolve(SomeRegisteredClass)

Usage

Registering Dependencies

# Transient: creates new object on each resolve
builder.register(User, User)

# Singleton: creates object only once
builder.singleton(User, User)

# Generics support
builder.singleton(Storage[int], Storage[int])
builder.singleton(Storage[str], Storage[str])

# String-based registration
builder.singleton('special_storage', Storage[int])

Factory Functions

The second argument is a factory, so you can provide any callable:

def create_db(config: Config) -> Database:
    return Database(config.dsn)

builder.singleton(Config, Config)
builder.singleton(Database, create_db)

Explicit Dependencies

Use Dependency to pass specific dependencies to a factory:

from independency import Dependency as Dep

builder.singleton('primary_db', Database, dsn='postgresql://primary')
builder.singleton('cache_db', Database, dsn='redis://cache')
builder.singleton(UserService, db=Dep('primary_db'))
builder.singleton(CacheService, db=Dep('cache_db'))

Testing with Overrides

Independency provides a specialized TestContainer for easy mocking in tests:

# Create your production container
container = builder.build()

# Create a test container with overrides
test_container = container.create_test_container()
test_container = test_container.with_overridden_singleton(
    Database,
    MockDatabase
)

# Use in tests
service = test_container.resolve(UserService)  # Gets MockDatabase injected

Examples

Basic HTTP Client

import requests
from independency import Container, ContainerBuilder


class Config:
    def __init__(self, url: str):
        self.url = url


class Getter:
    def __init__(self, config: Config):
        self.config = config

    def get(self):
        return requests.get(self.config.url)


def create_container() -> Container:
    builder = ContainerBuilder()
    builder.singleton(Config, Config, url='http://example.com')
    builder.singleton(Getter, Getter)
    return builder.build()


def main():
    container = create_container()
    getter: Getter = container.resolve(Getter)
    print(getter.get().status_code)


if __name__ == '__main__':
    main()

Multiple Instances with String Keys

When you need multiple instances of the same type:

from independency import Container, ContainerBuilder, Dependency as Dep


class Queue:
    def __init__(self, url: str):
        self.url = url

    def pop(self):
        ...


class Consumer:
    def __init__(self, q: Queue):
        self.queue = q

    def consume(self):
        while True:
            message = self.queue.pop()
            # process message


def create_container() -> Container:
    builder = ContainerBuilder()
    builder.singleton('first_q', Queue, url='http://example.com')
    builder.singleton('second_q', Queue, url='http://example2.com')
    builder.singleton('c1', Consumer, q=Dep('first_q'))
    builder.singleton('c2', Consumer, q=Dep('second_q'))
    return builder.build()


def main():
    container = create_container()
    consumer: Consumer = container.resolve('c1')
    consumer.consume()


if __name__ == '__main__':
    main()

API Reference

ContainerBuilder

  • register(key, factory, **kwargs) - Register a transient dependency (new instance per resolve)
  • singleton(key, factory, **kwargs) - Register a singleton dependency (single instance)
  • build() - Build and validate the container

Container

  • resolve(key) - Resolve a dependency by type or string key
  • create_test_container() - Create a test container from this container

TestContainer

  • with_overridden_singleton(key, factory, **kwargs) - Override a dependency for testing
  • resolve(key) - Resolve a dependency (same as Container)

Development

Setup

# Install uv if you don't have it
pip install uv

# Install dependencies
uv sync --extra dev

Running Tests

make tests  # Run pytest with 100% coverage requirement
make lint   # Run all linters (flake8, mypy, pylint, black)
make pretty # Format code with black

Requirements

  • Python 3.10 or higher
  • No runtime dependencies (only dev dependencies for testing)

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Ensure make tests and make lint pass
  5. Submit a pull request

If you find a bug or have a feature request, please open an issue on GitHub.

License

See the LICENSE file for details.

Credits

Inspired by punq - a similar dependency injection library for Python.

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

independency-1.4.tar.gz (5.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

independency-1.4-py3-none-any.whl (6.4 kB view details)

Uploaded Python 3

File details

Details for the file independency-1.4.tar.gz.

File metadata

  • Download URL: independency-1.4.tar.gz
  • Upload date:
  • Size: 5.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.13.11 Linux/6.11.0-1018-azure

File hashes

Hashes for independency-1.4.tar.gz
Algorithm Hash digest
SHA256 d892b6208a51e6e7fabd1fee56db365676597e0aa10106355a1dc3d87cf46ae7
MD5 57ce6fd449b9b27728b4ca47d770136f
BLAKE2b-256 7441ba9d8de6dcb00743edc2cdc9541697707aef03e8780dc99d0b348e093657

See more details on using hashes here.

File details

Details for the file independency-1.4-py3-none-any.whl.

File metadata

  • Download URL: independency-1.4-py3-none-any.whl
  • Upload date:
  • Size: 6.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.13.11 Linux/6.11.0-1018-azure

File hashes

Hashes for independency-1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 806f898730d1e07eb389e227f81179a3396a6fe855aa0a6dee09393087e183a6
MD5 144c39f643cb8f9723f037a0a26cda99
BLAKE2b-256 138f3119be5464a33ad4dbc7b50f4cbf371084de94a66f38dece8fceefda5137

See more details on using hashes here.

Supported by

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