# EDP Dependency Injection

## Overview

`EdpDependencyInjection` is a Python library for building and managing object dependencies using definitions and context-based injection. It provides a flexible way to construct, manage, and inject dependencies.

## Basic Concepts

- **Dependency**: Any class or object that your application needs, which can be automatically constructed and injected.
- **Dependency Context**: The environment that manages the lifecycle and resolution of dependencies. It is responsible for creating, storing, and disposing instances.
- **Dependency Loader**: The component that scans modules, registers classes, and builds the dependency context. It can also register custom builders and extra parameters.
- **Builder**: A function or class that defines custom logic for constructing or destroying dependencies. Builders allow you to override default instantiation.
- **Extra Parameters**: Custom values or objects that can be injected into dependencies during construction, either globally or for specific types.

## Installation

You can install the library using pip (if published):

```bash
pip install edpdependencyinjection
```

Or include the source in your project and ensure your PYTHONPATH includes the `src` directory.

once installed, the dependency loader can be instanciated
```python
from edpdependencyinjection import DependencyLoader
import my_module
import my_other_module

dependencies_loader = DependencyLoader()
dependencies_loader.load_modules([
    my_module,
    my_other_module
])
```
Or can be created by the global registry

```python
from edpdependencyinjection.registry import get_dependency_loader
import my_module
import my_other_module

dependencies_loader = get_dependency_loader() # Will create a dependency injection as singletion
dependencies_loader.load_modules([
    my_module,
    my_other_module
])
```

## Getting Started

### Basic Usage

Suppose you have a class `MyService` and want to manage its dependencies automatically:

```python
## Inside my_module
class MyService:
    def __init__(self, config: dict):
        self.config = config
```

```python
from edpdependencyinjection import DependencyLoader
import my_module
import my_other_module


# Register your module or class
dependencies_loader = DependencyLoader()
dependencies_loader.load_modules([
    my_module,
    my_other_module
])

dependencies_loader.add_dependency_extra_parameters({
    "config": {
        "key": "value"
    }
})

# Build the dependency context
context = dependencies_loader.build_context()

# Get an instance of MyService with injected config
service = context.get_instance(MyService)
print(service.config)  # {'key': 'value'}
```

### Dependency Builders

You can define custom builders to control how dependencies are constructed or disposed. This is useful for integrating with external libraries or managing complex object lifecycles.

An builder can be created by:

#### DependencyBuilder class

Using the DependencyBuilder class, the `build` method signature will be used to discover the return type of the dependency instance to be created.

By using this option, the dependency loader will automatically discover it, if inside the loaded modules.

```python
from edpdependencyinjection import DependencyBuilder

class MyDependencyBuilder(DependencyBuilder[MyDependency]):
    def build(self) -> MyDependency:
        return MyDependency()

    def destroy(self, instance: MyDependency) -> None:
        # Execute handler for destroy created instance
        return
```

Then, call the get instance of the builder type (Ex: `MyDependency`):

```python
from edpdependencyinjection import DependencyLoader
import my_module

dependencies_loader = DependencyLoader()
dependencies_loader.load_modules([my_module])

# ... (add extra parameters and build context as before)

context = dependencies_loader.build_context()
service = context.get_instance(MyDependency)
# MyDependency will now be built using MyDependencyBuilder
```

#### DependencyBuilder function

An dependency can be injected by an builder function. The return type will be use do discover the instance class.

Note that, by default, the dependency loader will not search for module functions, only class. Builders functions must be added to the dependency loader or context.

```python
def builder() -> ExternalSession:
    print("Building with function builder")
    return ExternalSession()
```

or using an generator function. Using this option, the yield value can be surrouded by an `try...finally` block to manage instance dispose.

```python

def builder() -> Generator[ExternalSession, None, None]:
    print("Building with yield function builder")
    session = ExternalSession()  # Create the session
    try:
        yield session  # Yield the session object for use
    finally:
        print("Closing session")
        session.close()  # Will be executed on dispose
```

Then, register the builder with the `DependencyLoader`:

```python
from edpdependencyinjection import DependencyLoader
import my_module

dependencies_loader = DependencyLoader()
dependencies_loader.add_builder(builder)
# ... (add extra parameters and build context as before)

context = dependencies_loader.build_context()
service = context.get_instance(ExternalSession)
# ExternalSession will be available3
```

## Advanced Features

- **Automatic Module Discovery**: Use `DependencyLoader.load_modules()` to scan and register all classes in a module.
- **Context Disposal**: Call `context.dispose()` to clean up all managed instances.
- **Support for Abstract Types**: Register abstract types and their implementations for flexible dependency resolution.

## API Reference

- `DependencyLoader`: Loads modules/classes, registers builders, builds contexts.
- `DependencyContext`: Resolves dependencies, manages instances, supports extra parameters and multiple keys.
- `DependencyBuilder`: Base class for custom construction/destruction logic.
- `DependencyMap`: Internal storage for classes, builders, instances, and parameters.
- Exceptions: `DependencyInjectionException`, `DependencyNotFoundException`, etc.

## Migration Notice

The legacy `dependencyinjection` module is deprecated. Please use `edpdependencyinjection` for all new development.

## Requirements

- Python 3.8+
- No external dependencies required for core usage.

## Author

Max Igor Guenes dos Santos
[max.santos@e-deploy.com.br](mailto:max.santos@e-deploy.com.br)
