Metadata-Version: 2.4
Name: kotinjection
Version: 0.0.3
Summary: Koin-like lightweight dependency injection container for Python with type inference support
License: MIT
License-File: LICENSE
Keywords: dependency-injection,di,ioc,inversion-of-control,koin,container,type-inference
Author: Vatis
Author-email: vatis@lalcs.com
Requires-Python: >=3.9
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Project-URL: Bug Tracker, https://github.com/Lalcs/kotinjection/issues
Project-URL: Changelog, https://github.com/Lalcs/kotinjection/releases
Project-URL: Documentation, https://github.com/Lalcs/kotinjection#readme
Project-URL: Homepage, https://github.com/Lalcs/kotinjection
Project-URL: Repository, https://github.com/Lalcs/kotinjection
Description-Content-Type: text/markdown

# KotInjection

[![image](https://img.shields.io/pypi/v/Kotinjection.svg)](https://pypi.org/project/Kotinjection/)
[![image](https://img.shields.io/pypi/l/Kotinjection.svg)](https://pypi.org/project/Kotinjection/)
[![image](https://img.shields.io/pypi/pyversions/Kotinjection.svg)](https://pypi.org/project/Kotinjection/)
[![image](https://img.shields.io/github/contributors/lalcs/Kotinjection.svg)](https://github.com/lalcs/Kotinjection/graphs/contributors)
[![image](https://img.shields.io/pypi/dm/Kotinjection)](https://pypistats.org/packages/Kotinjection)
[![Unit Tests](https://github.com/Lalcs/Kotinjection/actions/workflows/unittest.yml/badge.svg)](https://github.com/Lalcs/Kotinjection/actions/workflows/unittest.yml)

Koin-like Dependency Injection Container for Python

**KotInjection** is a lightweight DI (Dependency Injection) container library for Python, inspired by
Kotlin's [Koin](https://insert-koin.io/). It features type inference-based automatic dependency resolution and
Koin-style DSL syntax.

## Features

- **Simple API** - Intuitive DSL similar to Koin
- **Type Inference Support** - Automatic dependency resolution using Python type hints
- **Lifecycle Management** - Supports singleton and factory patterns
- **Eager Initialization** - Optional `created_at_start` for singleton pre-loading (like Koin)
- **Lazy Injection** - Class attribute lazy dependency injection (like Koin's `by inject()`)
- **Context Isolation** - Independent container instances for isolated DI contexts
- **Lightweight** - Pure Python implementation with no external dependencies
- **Type Safe** - Safe dependency management through type hints

## Installation

```bash
pip install kotinjection
```

## Quick Start

### Basic Usage

```python
from kotinjection import KotInjection, KotInjectionModule


# Define dependencies
class Database:
    def __init__(self):
        self.connection = "db://localhost"


class UserRepository:
    def __init__(self, db: Database):
        self.db = db

    def get_users(self):
        return f"Users from {self.db.connection}"


# Create a module
module = KotInjectionModule()
with module:
    module.single[Database](lambda: Database())
    module.single[UserRepository](
        lambda: UserRepository(db=module.get())
    )

# Initialize the DI container
KotInjection.start(modules=[module])

# Retrieve dependencies
repo = KotInjection.get[UserRepository]()
print(repo.get_users())  # "Users from db://localhost"

# Stop when done
KotInjection.stop()
```

### Lifecycle Management

```python
# Singleton (same instance is reused)
module.single[Database](lambda: Database())

# Factory (new instance created each time)
module.factory[RequestHandler](
    lambda: RequestHandler(repo=module.get())
)
```

### Eager Initialization

By default, singletons are lazily initialized on first access. Use `created_at_start=True` to initialize at `start()` time.

```python
# Definition level - specific singleton is eagerly initialized
module.single[Database](lambda: Database(), created_at_start=True)

# Module level - all singletons in this module are eagerly initialized
module = KotInjectionModule(created_at_start=True)
with module:
    module.single[Database](lambda: Database())  # Eager
    module.single[Cache](lambda: Cache(), created_at_start=False)  # Override: Lazy
```

### Lazy Injection (Class Attributes)

Similar to Koin's `by inject()`, you can define dependencies as class attributes that are resolved lazily on first access.

```python
from kotinjection import KotInjection

class MyService:
    # Dependency is resolved on first access, not at class definition time
    repository = KotInjection.inject[UserRepository]

    def get_users(self):
        return self.repository.get_users()

# Initialize after class definition
KotInjection.start(modules=[module])

# Dependency is resolved when accessed
service = MyService()
service.get_users()  # repository is resolved here
```

## Context Isolation

Context Isolation provides independent DI container instances that are completely separate from the global container.
Ideal for library development, multi-tenant applications, and test isolation.

### Basic Usage

```python
from kotinjection import KotInjectionCore, KotInjectionModule

# Create an isolated container instance
module = KotInjectionModule()
with module:
    module.single[MyService](lambda: MyService())

app = KotInjectionCore(modules=[module])

# Retrieve dependencies from the instance
service = app.get[MyService]()
```

### Use Case 1: Library Development

Create libraries that don't conflict with the host application's DI.

```python
from kotinjection import (
    KotInjectionCore,
    IsolatedKotInjectionComponent,
    KotInjectionModule
)

# Define a library-specific container
library_module = KotInjectionModule()
with library_module:
    library_module.single[LibraryRepository](lambda: LibraryRepository())

library_app = KotInjectionCore(modules=[library_module])


# Base class for library components
class LibraryComponent(IsolatedKotInjectionComponent):
    def get_app(self):
        return library_app


# Actual service class
class MyLibraryService(LibraryComponent):
    def __init__(self):
        # Get dependencies from the isolated container
        self.repository = self.get[LibraryRepository]()

    def do_something(self):
        return self.repository.fetch_data()
```

### Use Case 2: Multi-Tenant

Each tenant can have an independent DI environment.

```python
# Tenant 1's container
tenant1_app = KotInjectionCore(modules=[tenant1_module])

# Tenant 2's container
tenant2_app = KotInjectionCore(modules=[tenant2_module])

# Use independent dependencies for each tenant
tenant1_service = tenant1_app.get[Service]()
tenant2_service = tenant2_app.get[Service]()
```

### Use Case 3: Test Isolation

Use an independent DI container for each test case.

```python
import unittest
from kotinjection import KotInjectionCore


class TestMyService(unittest.TestCase):
    def test_with_isolated_container(self):
        # Test-specific isolated container
        with KotInjectionCore(modules=[test_module]) as app:
            service = app.get[MyService]()
            # Run tests...
        # Automatically cleaned up when exiting the context
```

## API Reference

For complete API documentation, see [API Reference](docs/api_reference.md).

### Quick Reference

| Class                           | Description                           |
|---------------------------------|---------------------------------------|
| `KotInjection`                  | Global DI container API               |
| `KotInjectionCore`              | Isolated container instance           |
| `KotInjectionModule`            | Dependency definitions container      |
| `IsolatedKotInjectionComponent` | Base class for isolated components    |
| `create_inject`                 | Create inject proxy for isolated containers |

### Key Methods

```python
# Global API
KotInjection.start(modules=[...])  # Initialize
KotInjection.get[Type]()  # Retrieve dependency (eager)
KotInjection.inject[Type]  # Lazy injection (class attribute)
KotInjection.stop()  # Cleanup

# Module Definition
module = KotInjectionModule(created_at_start=True)  # Eager init for all singletons
module.single[Type](factory)  # Singleton (lazy by default)
module.single[Type](factory, created_at_start=True)  # Singleton (eager)
module.factory[Type](factory)  # Factory
module.get()  # Type inference in factories

# Isolated Container
create_inject(app)  # Create inject proxy for isolated containers
```

## Advanced Usage

### Multiple Modules

```python
# Database module
db_module = KotInjectionModule()
with db_module:
    db_module.single[Database](lambda: Database())
    db_module.single[CacheService](lambda: CacheService())

# Repository module
repo_module = KotInjectionModule()
with repo_module:
    repo_module.single[UserRepository](
        lambda: UserRepository(
            db=repo_module.get(),
            cache=repo_module.get()
        )
    )

# Initialize with all modules
KotInjection.start(modules=[db_module, repo_module])
```

### Type Inference with Isolated Containers

```python
# Create the app first
app = KotInjectionCore()

# Use module.get() in module definitions
module = KotInjectionModule()
with module:
    module.single[Repository](lambda: Repository())
    module.single[Service](lambda: Service(repo=module.get()))

# Load modules
app.load_modules([module])

# Retrieve dependencies
service = app.get[Service]()
```

## Comparison with Koin

| Feature           | Koin (Kotlin)               | KotInjection (Python)                   |
|-------------------|-----------------------------|-----------------------------------------|
| DSL Syntax        | `single { }`, `factory { }` | `module.single[T]`, `module.factory[T]` |
| Type Inference    | `get()`                     | `module.get()`                          |
| Lazy Injection    | `by inject()`               | `KotInjection.inject[T]`                |
| Context Isolation | `koinApplication { }`       | `KotInjectionCore()`                    |
| Scope Management  | Multiple scopes             | Singleton/Factory only                  |
| Context Manager   | N/A                         | `with` statement support                |

## Development Guidelines

### Running Tests

```bash
python -m unittest discover tests
```

### Type Hints

All type hints are required for dependency resolution.

```python
class MyService:
    def __init__(self, repo: Repository):  # Type hint required
        self.repo = repo
```

## License

MIT License

## Acknowledgements

This project is inspired by Kotlin's [Koin](https://insert-koin.io/).


