Skip to main content

Spring style dependency injection for python

Project description

AppCtx

Spring-style dependency injection for Python

Python Version License PyPI Version

Overview

AppCtx is a lightweight dependency injection container inspired by the Spring Framework, providing a clean and elegant dependency management solution for Python applications. It makes it easy to manage dependencies and create maintainable, testable code.

Version: 0.1.2 Python Requirements: 3.8+
License: MIT

Features

  • 🚀 Easy to Use - Register and inject dependencies with simple decorators
  • 🔄 Auto-wiring - Automatic dependency resolution based on type annotations
  • 🏗️ Flexible Configuration - Support for both function and class bean definitions
  • 📦 Lightweight - Minimal dependencies, focused on core functionality
  • 🐍 Pythonic - API design that follows Python conventions
  • 🔧 Python 3.8+ - Compatible with Python 3.8, 3.9, 3.10, and 3.11
  • 🎯 Decorator-based bean registration - Simple @bean decorator for registration
  • 🔍 Circular dependency detection - Detects and reports circular dependencies

Installation

pip install appctx

Development Installation

For development, clone the repository and install with development dependencies:

git clone https://github.com/wssccc/appctx.git
cd appctx
pip install -e .[dev]

This will install the following development tools:

  • pytest>=7.0 - Testing framework
  • pytest-cov>=4.0 - Coverage reporting
  • black>=22.0 - Code formatting
  • flake8>=5.0 - Linting
  • mypy>=1.0 - Type checking
  • isort>=5.0 - Import sorting

Basic Concepts

AppCtx provides a simple way to manage dependencies in your Python applications:

  • Beans: Objects managed by the container
  • Container: The ApplicationContext that manages beans
  • Dependency Injection: Automatic wiring of dependencies based on type annotations

Quick Start

Basic Usage

from appctx import bean, get_bean, refresh

# Define service classes
class DatabaseService:
    def __init__(self, connection_string: str = "sqlite:///default.db"):
        self.connection_string = connection_string
    
    def connect(self):
        return f"Connected to {self.connection_string}"

class UserService:
    def __init__(self, db: DatabaseService):
        self.db = db
    
    def get_user(self, user_id: int):
        connection = self.db.connect()
        return f"User {user_id} from {connection}"

# Register beans using decorators
@bean
def database_service():
    return DatabaseService("postgresql://localhost/myapp")

@bean
def user_service(db: DatabaseService):  # Auto-inject DatabaseService
    return UserService(db)

# Initialize the container
refresh()

# Get and use beans
user_svc = get_bean(UserService)
print(user_svc.get_user(123))

Class Decorator Usage

from appctx import bean, get_bean, refresh

@bean
class EmailService:
    def __init__(self):
        self.server = "smtp.example.com"
    
    def send_email(self, to: str, subject: str):
        return f"Email sent to {to} via {self.server}"

@bean
class NotificationService:
    def __init__(self, email: EmailService):
        self.email = email
    
    def notify(self, user: str, message: str):
        return self.email.send_email(user, "Notification", message)

refresh()

notification_svc = get_bean(NotificationService)
print(notification_svc.notify("user@example.com", "Hello World!"))

How It Works

  1. Bean Registration: Use the @bean decorator to register functions that create beans
  2. Type Annotations: Use type annotations to declare dependencies
  3. Container Initialization: Call refresh() to initialize the container and resolve dependencies
  4. Bean Retrieval: Use get_bean() to retrieve beans by type or name

Advanced Usage

Custom Application Context

from appctx import ApplicationContext

# Create custom context
ctx = ApplicationContext()

@ctx.bean
def my_service():
    return MyService()

ctx.refresh()
service = ctx.get_bean(MyService)

Multiple Beans of Same Type

@bean
def primary_db():
    return DatabaseService("primary://db")

@bean
def secondary_db():
    return DatabaseService("secondary://db")

refresh()

# Get all database services
dbs = get_beans(DatabaseService)
print(f"Found {len(dbs)} database services")

# Get all beans of a specific type
databases = get_beans(DatabaseService)
print(len(databases))  # 2

Named Bean Retrieval

@bean
def database_service():
    return DatabaseService("app.db")

refresh()

# Get bean by name (function name)
db = get_bean("database_service")

API Reference

Core Decorators

@bean

Register a function or class as a bean.

@bean
def my_service():
    return MyService()

@bean
class MyComponent:
    def __init__(self, dependency: SomeDependency):
        self.dependency = dependency

Container Operations

refresh()

Initialize the container and instantiate all beans. Must be called before getting beans.

refresh()

get_bean(key)

Get a bean by type or name.

# Get by type
service = get_bean(MyService)

# Get by name
service = get_bean("my_service")

get_beans(type)

Get all beans of a specific type.

services = get_beans(MyService)

Dependency Resolution

AppCtx uses a sophisticated dependency resolution strategy that handles different parameter types:

  1. Positional Arguments - Resolved by type annotations only. Must have type annotations to be resolved.
  2. Keyword-only Arguments - Resolved by parameter name first, then use default values if available.
  3. **Variable Keyword Arguments (kwargs) - Injects all remaining beans that haven't been used as other parameters.
  4. Auto-wiring - Container automatically resolves and injects dependencies based on the above rules.
  5. Circular Dependency Detection - Detects and reports circular dependency issues.

Parameter Resolution Examples

@bean
def config_service():
    return "config_value"

@bean
def database_service():
    return "database_url"

# Positional args - resolved by type annotations
@bean
def service_with_positional(config_service: str):
    return f"Service: {config_service}"

# Keyword-only args - resolved by name
@bean
def service_with_keyword_only(*, config_service, timeout=30):
    return f"Service: {config_service}, timeout={timeout}"

# **kwargs - gets all remaining beans
@bean
def flexible_service(**kwargs):
    return f"Flexible: {kwargs}"

Error Handling

Common Errors

# Bean not found
try:
    service = get_bean(UnknownService)
except KeyError as e:
    print(f"Bean not found: {e}")

# Multiple beans of same type conflict
try:
    refresh()
except RuntimeError as e:
    print(f"Bean instantiation failed: {e}")

# Circular dependency
try:
    refresh()
except RuntimeError as e:
    print(f"Circular dependency detected: {e}")

Best Practices

  1. Use Type Annotations - Specify dependency types clearly for better code readability
  2. Single Responsibility - Each bean should have a clear responsibility
  3. Interface Abstraction - Use abstract base classes to define service interfaces
  4. Configuration Separation - Centralize bean configuration management
  5. Test-Friendly - Design beans that are easy to test

Development

Requirements

  • Python 3.8 or higher
  • pip

Running Tests

pytest tests/

Running Tests with Coverage

pytest --cov=src/appctx tests/

Code Formatting

Format code with Black (line length: 88):

black src/ tests/

Import Sorting

Sort imports with isort (Black profile):

isort src/ tests/

Linting

Check code quality with flake8:

flake8 src/ tests/

Type Checking

Run type checking with mypy:

mypy src/

Run All Quality Checks

# Format code
black src/ tests/
isort src/ tests/

# Run linting and type checking
flake8 src/ tests/
mypy src/

# Run tests with coverage
pytest --cov=src/appctx tests/

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

We welcome contributions! Please see our contributing guidelines below.

Development Setup

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Create a Pull Request

Running Tests

# Run all tests
pytest

# Run tests with coverage
pytest --cov=appctx

# Run linting
black --check src/ tests/
flake8 src/ tests/
mypy src/

Release Process

For maintainers, see RELEASE.md for detailed release instructions.

Links

Changelog

v0.1.0

  • Initial release
  • Basic dependency injection functionality
  • Decorator API
  • Auto-wiring support
  • Python 3.8+ support

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

appctx-0.1.2.tar.gz (13.1 kB view details)

Uploaded Source

Built Distribution

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

appctx-0.1.2-py3-none-any.whl (7.8 kB view details)

Uploaded Python 3

File details

Details for the file appctx-0.1.2.tar.gz.

File metadata

  • Download URL: appctx-0.1.2.tar.gz
  • Upload date:
  • Size: 13.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for appctx-0.1.2.tar.gz
Algorithm Hash digest
SHA256 7b209295e915c8cfd4f2de0ad292daf89da1fcddbacb627edb241ec39555ada6
MD5 c86d7d19ca9a9871ce87b238102333c9
BLAKE2b-256 3227fed698618d68fb8139f442f82c74a1e6895c5cb59d4b8666d64a17967764

See more details on using hashes here.

Provenance

The following attestation bundles were made for appctx-0.1.2.tar.gz:

Publisher: publish.yml on wssccc/appctx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file appctx-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: appctx-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 7.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for appctx-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 46c7317b801cbda67684673572cb4edcab178dcfe7dc2e2abf63c02cf8a0e3b2
MD5 fedf3c5f269abf53e7aecbc8ebf59c04
BLAKE2b-256 e18668c4eb05d0419e62181d446431598ead45e5ad608260de44c1200b6a4d12

See more details on using hashes here.

Provenance

The following attestation bundles were made for appctx-0.1.2-py3-none-any.whl:

Publisher: publish.yml on wssccc/appctx

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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