Skip to main content

Modern Python dependency injection library combining simplicity, power, and performance

Project description

InjectQ

PyPI version Python versions License Coverage

InjectQ is a modern, lightweight Python dependency injection library focused on clarity, type-safety, and seamless framework integration.

Documentation

Full documentation is hosted at Documentation and the repository docs/ contains the source.

Key features

  • Simplicity-first dict-like API for quick starts
  • Flexible decorator- and type-based injection (@inject, Inject[T])
  • Type-friendly: designed to work with static type checkers
  • Built-in integrations for frameworks (FastAPI, Taskiq) as optional extras
  • Factory and async factory support
  • 🆕 Hybrid factory methods combining DI with manual arguments (invoke(), ainvoke())
  • Scope management and testing utilities

Quick Start (recommended pattern)

Prefer the exported global InjectQ.get_instance() container in examples and application code. It uses the active context container when present, otherwise falls back to a global singleton.

from injectq import InjectQ, inject, singleton

container = InjectQ.get_instance()

# Basic value binding
container[str] = "Hello, World!"

@singleton
class UserService:
    def __init__(self, message: str):
        self.message = message

    def greet(self) -> str:
        return f"Service says: {self.message}"

@inject
def main(service: UserService) -> None:
    print(service.greet())

if __name__ == "__main__":
    main()  # Prints: Service says: Hello, World!

Notes:

  • Use container[...] for simple bindings and values.
  • Use @inject and Inject[T] for function/class injection.

Enhanced Features

Nullable Dependencies

InjectQ supports binding None values for optional dependencies using the allow_none parameter:

from injectq import InjectQ

container = InjectQ()

# Optional service - can be None
class EmailService:
    def send_email(self, to: str, message: str) -> str:
        return f"Email sent to {to}: {message}"

class NotificationService:
    def __init__(self, email_service: EmailService | None = None):
        self.email_service = email_service

    def notify(self, message: str) -> str:
        if self.email_service:
            return self.email_service.send_email("user", message)
        return f"Basic notification: {message}"

# Bind None for optional dependency
container.bind(EmailService, None, allow_none=True)
container.bind(NotificationService, NotificationService)

service = container.get(NotificationService)
print(service.notify("Hello"))  # Prints: Basic notification: Hello

Abstract Class Validation

InjectQ automatically prevents binding abstract classes and raises a BindingError during binding (not at resolution time):

from abc import ABC, abstractmethod
from injectq import InjectQ
from injectq.utils.exceptions import BindingError

class PaymentProcessor(ABC):  # Abstract class
    @abstractmethod
    def process_payment(self, amount: float) -> str:
        pass

class CreditCardProcessor(PaymentProcessor):  # Concrete implementation
    def process_payment(self, amount: float) -> str:
        return f"Processing ${amount} via credit card"

container = InjectQ()

# This will raise BindingError immediately
try:
    container.bind(PaymentProcessor, PaymentProcessor)  # Error!
except BindingError:
    print("Cannot bind abstract class")

# This works fine
container.bind(PaymentProcessor, CreditCardProcessor)  # OK

See examples/enhanced_features_demo.py for a complete demonstration.

🆕 Hybrid Factory Methods

The new invoke() and ainvoke() methods combine dependency injection with manual arguments:

from injectq import InjectQ

container = InjectQ()
container.bind(Database, Database)
container.bind(Cache, Cache)

# Factory that needs both DI dependencies and runtime arguments
def create_user_service(db: Database, cache: Cache, user_id: str):
    return UserService(db, cache, user_id)

container.bind_factory("user_service", create_user_service)

# ❌ Old way - verbose
db = container[Database]
cache = container[Cache]
service = container.call_factory("user_service", db, cache, "user123")

# ✅ New way - automatic DI + manual args
service = container.invoke("user_service", user_id="user123")
# Database and Cache auto-injected, only provide user_id!

# Also works with async
service = await container.ainvoke("async_service", batch_size=100)

When to use invoke():

  • Factory needs some DI dependencies + some runtime arguments
  • You want cleaner code without manual resolution
  • Mix configuration from container with user input

See examples/factory_api_showcase.py and docs/injection-patterns/factory-methods.md for details.

Installation

Install from PyPI:

pip install injectq

Optional framework integrations (install only what you need):

pip install injectq[fastapi]   # FastAPI integration (optional)
pip install injectq[taskiq]    # Taskiq integration (optional)

Where to look next

  • docs/getting-started/installation.md — installation and verification
  • docs/injection-patterns/dict-interface.md — dict-like API
  • docs/injection-patterns/inject-decorator.md@inject usage
  • docs/injection-patterns/factory-methods.md — factory patterns (DI, parameterized, hybrid)
  • docs/integrations/ — integration guides for FastAPI and Taskiq

License

MIT — see the LICENSE file.

Run tests with coverage

Activate the project's virtualenv and run pytest (coverage threshold is configured to 73%):

source .venv/bin/activate
python -m pytest

Coverage reports are written to htmlcov/ and coverage.xml.

Performance Benchmarks

InjectQ includes comprehensive performance benchmarks to ensure production-ready performance:

# Run all benchmarks
pytest tests/test_benchmarks.py --benchmark-only

# Run with verbose statistics
pytest tests/test_benchmarks.py --benchmark-only --benchmark-verbose

# Save results for comparison
pytest tests/test_benchmarks.py --benchmark-only --benchmark-autosave

Performance Highlights

  • Ultra-fast operations: Basic operations (bind, get, has) execute in 270-780 nanoseconds
  • Efficient resolution: Dependency resolution completes in ~1 microsecond
  • Excellent scalability: Handles 1,000+ operations with sub-millisecond performance
  • Thread-safe: Concurrent access with minimal overhead (~24 μs)
  • Production-ready: Web request simulation (10 services) completes in 142 microseconds

📊 See BENCHMARK_REPORT.md for detailed analysis and BENCHMARK_QUICK_GUIDE.md for usage guide.

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

injectq-0.4.1.tar.gz (3.2 MB view details)

Uploaded Source

Built Distribution

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

injectq-0.4.1-py3-none-any.whl (74.0 kB view details)

Uploaded Python 3

File details

Details for the file injectq-0.4.1.tar.gz.

File metadata

  • Download URL: injectq-0.4.1.tar.gz
  • Upload date:
  • Size: 3.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.9

File hashes

Hashes for injectq-0.4.1.tar.gz
Algorithm Hash digest
SHA256 de383debdb1591d4be9e2164e5ba1686af4ea0686885b3d085c3126b3b42bc31
MD5 1c8f98b9c43356ab6febd05f4f770c81
BLAKE2b-256 72786c7b66d0c9ff30a61baa196a3a4d2621d890c9bbe1b081781ff0797f13d8

See more details on using hashes here.

File details

Details for the file injectq-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: injectq-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 74.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.9

File hashes

Hashes for injectq-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6fd6dc56e424ba1a4d5f2595c4cd71c33846a7c13d04ac18e5de4f5bfb45c79c
MD5 8e376a71c0e457e2528fe63955eba8f6
BLAKE2b-256 65a6cbfd571bd34671f66e88da411abb4a6384fcbd60831a28909f5028513bf5

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