Skip to main content

๐Ÿฆ† A minimal dependency injection library for Python

Project description

DuckDI Logo

๐Ÿฆ† DuckDI

DuckDI is a minimal, type-safe, and architecture-friendly dependency injection library for Python.

It provides a clean interface to register and resolve dependencies at runtime using a TOML-based configuration, following the duck typing principle: "if it implements the expected methods, itโ€™s good enough."
Ideal for developers who want clarity, zero magic, and full control over dependency resolution.


๐Ÿš€ Features

  • โœ… Clean and lightweight API
  • โœ… Zero runtime dependencies
  • โœ… Fully type-safe (no introspection magic)
  • โœ… Supports singleton and transient resolution
  • โœ… Uses TOML to bind interfaces to adapters
  • โœ… Works with ABC and regular classes (no need for Protocols)
  • โœ… Clear and informative error messages
  • โœ… Environment-based configuration (INJECTIONS_PATH)

๐Ÿ“ฆ Installation

With Poetry:

poetry add duckdi

Or using pip:

pip install duckdi

๐Ÿ› ๏ธ Usage

1. Define an interface

from duckdi import Interface
from abc import ABC, abstractmethod

@Interface
class IUserRepository(ABC):
    @abstractmethod
    def get_user(self, user_id: str) -> dict: ...

2. Register an adapter

from duckdi import register

class PostgresUserRepository(IUserRepository):
    def get_user(self, user_id: str) -> dict:
        return {"id": user_id, "name": "John Doe"}

register(PostgresUserRepository)

You can also register it as a singleton:

register(PostgresUserRepository, is_singleton=True)

3. Create your injection payload

Create a file called injections.toml:

[injections]
"i_user_repository" = "postgres_user_repository"

4. Set the environment variable

Set the injection file path using the INJECTIONS_PATH environment variable:

export INJECTIONS_PATH=./injections.toml

5. Resolve your dependencies

from duckdi import Get

repo = Get(IUserRepository)
user = repo.get_user("123")
print(user)  # {'id': '123', 'name': 'John Doe'}

๐Ÿ’ฅ Error Handling

MissingInjectionPayloadError

Raised when no injection payload file is found at the specified path.

InvalidAdapterImplementationError

Raised when the adapter registered does not implement the expected interface.

InterfaceAlreadyRegisteredError

Raised when trying to register the same interface twice.

AdapterAlreadyRegisteredError

Raised when the same adapter is registered more than once.


๐Ÿ“ Project Structure

duckdi/
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ duckdi/
โ”‚       โ”œโ”€โ”€ __init__.py
โ”‚       โ”œโ”€โ”€ cli.py
โ”‚       โ”œโ”€โ”€ duck.py
โ”‚       โ”œโ”€โ”€ errors/
โ”‚       โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚       โ”‚   โ”œโ”€โ”€ invalid_adapter_implementation_error.py
โ”‚       โ”‚   โ”œโ”€โ”€ interface_already_registered_error.py
โ”‚       โ”‚   โ”œโ”€โ”€ adapter_already_registered_error.py
โ”‚       โ”‚   โ””โ”€โ”€ missing_injection_payload_error.py
โ”‚       โ”œโ”€โ”€ utils/
โ”‚       โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚       โ”‚   โ”œโ”€โ”€ buffer_readers.py
โ”‚       โ”‚   โ””โ”€โ”€ to_snake.py
โ”‚       โ””โ”€โ”€ injections/
โ”‚           โ”œโ”€โ”€ injections_container.py
โ”‚           โ””โ”€โ”€ injections_payload.py
โ””โ”€โ”€ tests/
    โ”œโ”€โ”€ test_interface.py
    โ”œโ”€โ”€ test_register.py
    โ””โ”€โ”€ test_get.py

๐Ÿงฉ Advanced Example

You can register multiple adapters and resolve them dynamically based on the TOML mapping:

from duckdi import Interface, register, Get

@Interface
class INotifier:
    def send(self, msg: str): ...

class EmailNotifier(INotifier):
    def send(self, msg: str):
        print(f"Sending email: {msg}")

register(EmailNotifier)

# injections.toml
# [injections]
# "i_notifier" = "email_notifier"

notifier = Get(INotifier)
notifier.send("Hello from DuckDI!")

๐Ÿงช Testing

To run tests:

pytest

Or via Makefile:

make test

To check static typing:

make check

๐Ÿ“„ License

Licensed under the MIT License.
See the LICENSE file for more information.


๐Ÿ‘ค Author

Made with โค๏ธ by PhePato
Pull requests, issues and ideas are always welcome!

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

duckdi-0.1.9.tar.gz (6.3 kB view details)

Uploaded Source

Built Distribution

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

duckdi-0.1.9-py3-none-any.whl (11.2 kB view details)

Uploaded Python 3

File details

Details for the file duckdi-0.1.9.tar.gz.

File metadata

  • Download URL: duckdi-0.1.9.tar.gz
  • Upload date:
  • Size: 6.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.12.0 Linux/6.8.0-60-generic

File hashes

Hashes for duckdi-0.1.9.tar.gz
Algorithm Hash digest
SHA256 6b14fd2333661a114fbe3beaf5faa90ecb828e21006a5a022e3db2c356bcb10d
MD5 6caa0dcdc3a983460a414b773484666c
BLAKE2b-256 6528a6aa2fb950c1c9bf6b55b74d6c2811b4f3e19f5ec6aaef676bdb92a60bad

See more details on using hashes here.

File details

Details for the file duckdi-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: duckdi-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 11.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.2 CPython/3.12.0 Linux/6.8.0-60-generic

File hashes

Hashes for duckdi-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 f93d370261a0de8ca4b2f4423624b8148e9cd707a3829ce1574878288469c411
MD5 a32371af7758f43f9c98a9b037151d1c
BLAKE2b-256 3254d3a62d58b7acabdab9ae4ef5d5ff88237b58ca3996684010fc492042c27d

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