Skip to main content

Simple Dependency Injection for Python

Project description

Picodi - Python DI (Dependency Injection) Library

Build Status Codecov PyPI - Version PyPI - Python Version PyPI - Downloads Documentation Status

Documentation

Picodi simplifies Dependency Injection (DI) for Python applications. DI is a design pattern that allows objects to receive their dependencies from an external source rather than creating them internally. This library supports both synchronous and asynchronous contexts, and offers features like lifecycle management.

Table of Contents

Status

Picodi is currently in the experimental stage. Public APIs may change without notice until the library reaches a 1.x.x version.

Installation

pip install picodi

Features

  • 🌟 Simple and lightweight
  • 📦 Zero dependencies
  • ⏱️ Supports both sync and async contexts
  • 🔄 Lifecycle management
  • 🔍 Type hints support
  • 🐍 Python & PyPy 3.10+ support
  • 🚀 Works well with FastAPI
  • 🧪 Integration with pytest

Quick Start

import asyncio
from collections.abc import Callable
from datetime import date
from typing import Any

import httpx

from picodi import (
    Provide,
    inject,
    registry,
    SingletonScope,
)
from picodi.helpers import get_value


# Regular functions without required arguments can be used as a dependency
def get_settings() -> dict:
    return {
        "nasa_api": {
            "api_key": "DEMO_KEY",
            "base_url": "https://api.nasa.gov",
            "timeout": 10,
        }
    }


# Helper function to get a setting from the settings dictionary.
# We can use this function to inject specific settings, not the whole settings object.
@inject
def get_setting(path: str, settings: dict = Provide(get_settings)) -> Callable[[], Any]:
    value = get_value(path, settings)
    return lambda: value


# We want to reuse the same client for all requests, so we declare a dependency
#   with `SingletonScope` that provides an httpx.AsyncClient instance
#   with the correct settings.
@registry.set_scope(
    scope_class=SingletonScope,
    auto_init=True,
)
@inject
async def get_nasa_client(
    api_key: str = Provide(get_setting("nasa_api.api_key")),
    base_url: str = Provide(get_setting("nasa_api.base_url")),
    timeout: int = Provide(get_setting("nasa_api.timeout")),
) -> httpx.AsyncClient:
    async with httpx.AsyncClient(
        base_url=base_url, params={"api_key": api_key}, timeout=timeout
    ) as client:
        yield client


@inject
async def get_apod(
    date: date, client: httpx.AsyncClient = Provide(get_nasa_client)
) -> dict[str, Any]:
    # Printing the client ID to show that the same client is reused for all requests.
    print("Client ID:", id(client))
    response = await client.get("/planetary/apod", params={"date": date.isoformat()})
    response.raise_for_status()
    return response.json()


@inject
# Note that asynchronous `get_nasa_client` is injected
#  in synchronous `print_client_info` function.
def print_client_info(client: httpx.AsyncClient = Provide(get_nasa_client)):
    print("Client ID:", id(client))
    print("Client Base URL:", client.base_url)
    print("Client Params:", client.params)
    print("Client Timeout:", client.timeout)


# `lifespan` will initialize dependencies on the application startup.
#   This will create the
#   httpx.AsyncClient instance and cache it for later use. Thereby, the same
#   client will be reused for all requests. This is important for connection
#   pooling and performance. Because it's created on app startup,
#   it will allow to pass asynchronous `get_nasa_client` into synchronous functions.
# And closing all inited dependencies after the function execution.
@registry.alifespan()
async def main():
    print_client_info()

    apod_data = await get_apod(date(2011, 7, 19))
    print("Title:", apod_data["title"])

    apod_data = await get_apod(date(2011, 7, 26))
    print("Title:", apod_data["title"])


asyncio.run(main())
# Client ID: 4334576784
# Client Base URL: https://api.nasa.gov
# Client Params: api_key=DEMO_KEY
# Client Timeout: Timeout(timeout=10)
#
# Client ID: 4334576784
# Title: Vesta Vista
#
# Client ID: 4334576784
# Title: Galaxy NGC 474: Cosmic Blender

FastAPI Example Project

Here is an example of a FastAPI application that uses Picodi for dependency injection:

Picodi FastAPI Example

License

MIT

Contributing

Contributions are welcome! Please read the CONTRIBUTING.md file for more information.

Credits

This project was generated with yakimka/cookiecutter-pyproject.

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

picodi-0.36.3.tar.gz (24.7 kB view details)

Uploaded Source

Built Distribution

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

picodi-0.36.3-py3-none-any.whl (24.5 kB view details)

Uploaded Python 3

File details

Details for the file picodi-0.36.3.tar.gz.

File metadata

  • Download URL: picodi-0.36.3.tar.gz
  • Upload date:
  • Size: 24.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"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 picodi-0.36.3.tar.gz
Algorithm Hash digest
SHA256 47dd2717c32950c8f0722559d624bdd2782ea68b3e3e4b6b7aa82d45a1a935ef
MD5 e547e3424115155d4b9e5f55c6a8f858
BLAKE2b-256 24709f92d43fa76fafd11d9525bfc495d982984526481c729737429c3ef81798

See more details on using hashes here.

File details

Details for the file picodi-0.36.3-py3-none-any.whl.

File metadata

  • Download URL: picodi-0.36.3-py3-none-any.whl
  • Upload date:
  • Size: 24.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"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 picodi-0.36.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ef9a8ee906dbfae565ab44a888fc079197dcf24291ff11cad54a2fe2a1a9cd3c
MD5 7e7e09b92f7b5436bb27ad220567cdd7
BLAKE2b-256 8b2e85e6268cc4ac1ff8080f5d1653125db40527b9b8b991612cc4287919dd08

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