Skip to main content

A simple and lightweight dependency injection container for Python

Project description

PyDI

A simple and lightweight dependency injection container for Python with support for different service lifetimes, automatic dependency resolution, and circular dependency detection.

Features

  • Multiple Service Lifetimes: Singleton, Scoped, and Transient
  • Automatic Dependency Resolution: Uses type hints to automatically inject dependencies
  • Circular Dependency Detection: Detects and prevents circular dependencies at registration time
  • Lazy Initialization: Services are instantiated only when needed
  • Service Factories: Create multiple instances on demand using ServiceFactory[T]
  • Type Safety: Full type hint support for better IDE integration and type checking

Installation

pip install pydi-core

Quick Start

from pydi_core import PyDI

class Database:
    def __init__(self):
        self.connection = "Connected to database"

class UserRepository:
    def __init__(self, database: Database):
        self.database = database
    
    def get_users(self):
        return f"Fetching users using {self.database.connection}"

class UserService:
    def __init__(self, repository: UserRepository):
        self.repository = repository
    
    def list_users(self):
        return self.repository.get_users()

# Create container and register services
pydi = PyDI()
pydi.register_service(Database, PyDI.LIFETIME_SINGLETON)
pydi.register_service(UserRepository, PyDI.LIFETIME_SCOPED)
pydi.register_service(UserService, PyDI.LIFETIME_TRANSIENT)

# Get service with dependencies automatically injected
user_service = pydi.get_service(UserService)
print(user_service.list_users())
# Output: Fetching users using Connected to database

Service Lifetimes

Singleton

The service is created only once and the same instance is returned every time.

pydi.register_service(Database, PyDI.LIFETIME_SINGLETON)

Scoped

The service is created once per dependency resolution scope. Within a single get_service() call, the same instance is reused for all dependencies that need it, but a new instance is created for each top-level get_service() call.

pydi.register_service(UserRepository, PyDI.LIFETIME_SCOPED)

Transient

A new service instance is created every time it is requested.

pydi.register_service(UserService, PyDI.LIFETIME_TRANSIENT)

Service Factories

Use ServiceFactory[T] to create multiple instances of a service on demand:

from pydi_core import PyDI, ServiceFactory

class Connection:
    def __init__(self):
        self.id = id(self)

class ConnectionPool:
    def __init__(self, connection_factory: ServiceFactory[Connection]):
        self.factory = connection_factory
        self.connections = [
            self.factory.create_instance(),
            self.factory.create_instance(),
            self.factory.create_instance()
        ]

pydi = PyDI()
pydi.register_service(Connection, PyDI.LIFETIME_TRANSIENT)
pydi.register_service(ConnectionPool, PyDI.LIFETIME_SINGLETON)

pool = pydi.get_service(ConnectionPool)
# pool.connections contains 3 different Connection instances

Error Handling

PyDI provides specific exceptions for different error scenarios:

CircularDependencyError

Raised when a circular dependency is detected:

class ServiceA:
    def __init__(self, service_b: 'ServiceB'):
        self.service_b = service_b

class ServiceB:
    def __init__(self, service_a: ServiceA):
        self.service_a = service_a

pydi = PyDI()
pydi.register_service(ServiceA, PyDI.LIFETIME_SINGLETON)
pydi.register_service(ServiceB, PyDI.LIFETIME_SINGLETON)
# Raises CircularDependencyError when finalized

UnknownDependencyError

Raised when a required dependency is not registered:

class ServiceA:
    def __init__(self, service_b: ServiceB):
        self.service_b = service_b

pydi = PyDI()
pydi.register_service(ServiceA, PyDI.LIFETIME_SINGLETON)
# Raises UnknownDependencyError - ServiceB not registered

AlreadyFinalizedError

Raised when trying to register a service after finalization:

pydi = PyDI()
pydi.register_service(ServiceA, PyDI.LIFETIME_SINGLETON)
pydi.finalize()  # Manual finalization
# Raises AlreadyFinalizedError if registering after this point

InvalidLifetimeError

Raised when an invalid lifetime value is provided:

pydi.register_service(ServiceA, 999)  # Invalid lifetime
# Raises InvalidLifetimeError

API Reference

PyDI

__init__()

Create a new dependency injection container.

register_service(service: Type, lifetime: int) -> None

Register a service with the specified lifetime.

Parameters:

  • service: The service class to register
  • lifetime: One of LIFETIME_SINGLETON, LIFETIME_SCOPED, or LIFETIME_TRANSIENT

Raises:

  • AlreadyFinalizedError: If called after finalization
  • InvalidLifetimeError: If lifetime is invalid
  • UnknownDependencyError: If a dependency annotation is missing

finalize() -> None

Finalize the container and validate all dependencies. This is called automatically when getting a service, so manual calling is optional.

Raises:

  • CircularDependencyError: If a circular dependency is detected
  • UnknownDependencyError: If a dependency is not registered

get_service(service: Type[T]) -> T

Get an instance of the registered service with dependencies injected.

Parameters:

  • service: The service class to retrieve

Returns:

  • An instance of the service

Raises:

  • CircularDependencyError: If a circular dependency is detected
  • UnknownDependencyError: If the service or dependency is not registered

ServiceFactory[T]

create_instance() -> T

Create a new instance of the service, respecting its registered lifetime.

Returns:

  • A new instance of the service (or cached instance for singletons)

Requirements

  • Python >= 3.11

License

This project is licensed under the Apache License 2.0 — see LICENSE.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Author

Lorenzo Brilli

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

pydi_core-1.0.0.tar.gz (8.9 kB view details)

Uploaded Source

Built Distribution

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

pydi_core-1.0.0-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file pydi_core-1.0.0.tar.gz.

File metadata

  • Download URL: pydi_core-1.0.0.tar.gz
  • Upload date:
  • Size: 8.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pydi_core-1.0.0.tar.gz
Algorithm Hash digest
SHA256 08009cb02d4759276e1ba4e5f0ee32e57bed8d654b59fabe9a30a13646bab373
MD5 90d6784a7a1adfb27326820b49c627ce
BLAKE2b-256 b130f46c8440783ba10ac0e145318972e31e1a99002176d4609cfc6012e0663a

See more details on using hashes here.

File details

Details for the file pydi_core-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pydi_core-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 10.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for pydi_core-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a8c14193566d1c5790e194481e2ed3af27246c9f496ba5cc3636fa9020c59196
MD5 49f60ca2c5823b1a4e4cf4925f743988
BLAKE2b-256 3dccdbdc0029b14cb2bf04390391296de822ee7a3efc33fd4fbfb4e69138f8a5

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