DEV Community

Cover image for Announcing Dioxide v1.0.0: Delightful Dependency Injection for Python
Mike Lane
Mike Lane

Posted on

Announcing Dioxide v1.0.0: Delightful Dependency Injection for Python

Dioxide v1.0.0 is Here! πŸŽ‰

After months of development, I'm thrilled to announce Dioxide v1.0.0 my opinionated, ergonomic dependency injection framework for Python backed by Rust.

What is Dioxide?

Dioxide makes the Dependency Inversion Principle feel inevitable. It's designed to make depending on abstractions (ports) instead of implementations (adapters) trivially easy.

from dioxide import service, adapter, Container, Profile
from typing import Protocol

# Define your port (interface)
class EmailPort(Protocol):
    def send(self, to: str, body: str) -> None: ...

# Business logic depends on abstractions, not infrastructure
@service
class NotificationService:
    def __init__(self, email: EmailPort) -> None:
        self.email = email

    def notify_user(self, user_id: str, message: str) -> None:
        self.email.send(f"{user_id}@example.com", message)

# Production adapter - the only place with real infrastructure
@adapter.for_(EmailPort, profile=Profile.PRODUCTION)
class SendGridAdapter:
    def send(self, to: str, body: str) -> None:
        # Real SendGrid implementation
        pass

# Test adapter - fast, in-memory
@adapter.for_(EmailPort, profile=Profile.TEST)
class FakeEmailAdapter:
    sent: list[tuple[str, str]] = []

    def send(self, to: str, body: str) -> None:
        self.sent.append((to, body))
Enter fullscreen mode Exit fullscreen mode

Why Dioxide?

πŸ¦€ Rust-Backed Core

The container internals are written in Rust via PyO3. Why? Because dependency graphs and cycle detection are a perfect fit for Rust's strong type system. The Python API stays Pythonic; Rust handles the graph theory.

🐍 Pythonic API

No XML. No YAML. No Java patterns. Just decorators, type hints, and Protocols β€” the Python way.

πŸ§ͺ Testing is Architecture

Dioxide's profile system makes testing a first-class citizen. Swap implementations with a single parameter:

# Production
container = Container(profile=Profile.PRODUCTION)
container.scan(packages=["myapp"])

# Tests - same code, different adapters
container = Container(profile=Profile.TEST)
container.scan(packages=["myapp"])
Enter fullscreen mode Exit fullscreen mode

πŸ”Œ Framework Integrations

v1.0.0 ships with integrations for the most popular Python frameworks:

Framework Install Features
FastAPI pip install dioxide[fastapi] Middleware, Inject() helper
Django pip install dioxide[django] Middleware, inject() in views
Django REST Framework pip install dioxide[drf] Works with APIView, ViewSet, @api_view
Django Ninja pip install dioxide[ninja] Sync and async endpoints
Flask pip install dioxide[flask] Request hooks, inject() helper
Celery pip install dioxide[celery] Task scoping with scoped_task
Click pip install dioxide[click] CLI command scoping

Quick Start with Django

# settings.py
MIDDLEWARE = [
    "dioxide.django.DioxideMiddleware",
    # ... other middleware
]

# apps.py
from dioxide.django import configure_dioxide
from dioxide import Profile

class MyAppConfig(AppConfig):
    def ready(self):
        configure_dioxide(
            packages=["myapp.services"],
            profile=Profile.PRODUCTION,
        )

# views.py
from dioxide.django import inject

def my_view(request):
    service = inject(MyService)
    return JsonResponse({"result": service.do_work()})
Enter fullscreen mode Exit fullscreen mode

The Philosophy: Fakes Over Mocks

Dioxide encourages fakes over mocks for testing. Instead of complex mock configurations that test mock behavior, use simple in-memory implementations that test real behavior:

@adapter.for_(UserRepository, profile=Profile.TEST)
class FakeUserRepository:
    def __init__(self):
        self.users: dict[str, User] = {}

    def save(self, user: User) -> None:
        self.users[user.id] = user

    def find(self, user_id: str) -> User | None:
        return self.users.get(user_id)
Enter fullscreen mode Exit fullscreen mode

Your tests become simpler, more reliable, and actually test your business logic.

Installation

# Core only
pip install dioxide

# With your framework of choice
pip install dioxide[fastapi]
pip install dioxide[django]
pip install dioxide[flask]

# Multiple frameworks
pip install dioxide[fastapi,celery]
Enter fullscreen mode Exit fullscreen mode

What's Next?

v1.0.0 marks the completion of our Minimum Lovable Product. The API is now stable β€” no breaking changes until v2.0.

We're exploring:

  • Multi-binding / collection injection for plugin patterns
  • Enhanced async support
  • More framework integrations

Get Involved


Dioxide is open source under the MIT license. Contributions welcome!

Top comments (0)