Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [v1.1.1] - 2026-02-20

## [v1.0.0] - 2026-02-
### Added

- Global registry for managing and validating injectables and providers

### Changed

- Updated documentation and examples to reflect recent changes
- Injectables have been extracted from Injection and now are handled by Providers
- Refactored resolution logic to use a registry for better management and validation
- On components, products declaration has been removed, use imports declaration

## [v1.1.0] - 2026-02-13

### Fixed

- Fixed Products not being exported from the package

## [v1.0.0] - 2026-02-13

### Added

Expand Down
9 changes: 3 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,8 @@ from ...plugin...other_component import OtherService
from ...plugin...........product import SomeProduct

@instance(
imports=[OtherService, ...], # List of dependencies (components) that are needed
products=[SomeProduct, ...], # List of products that this instance will create
provider=providers.Singleton, # Provider type (Singleton, Factory, Resource)
imports=[OtherService, ...], # List of dependencies (components) that this product needs
provider=providers.Singleton, # Provider type from di (Singleton, Factory, Resource)
bootstrap=False, # Whether to bootstrap on application start
)
class ImplementedSomeService(SomeService):
Expand Down Expand Up @@ -183,8 +182,7 @@ from ...plugin.....other_product import OtherProduct

@product(
module=SomeModule, # Declares the module or plugin this component belongs to
imports=[SomeService, ...], # List of dependencies (components) that are needed
products=[OtherProduct, ...], # List of products that this product will create
imports=[SomeService, ...], # List of dependencies (components) that this product needs
provider=providers.Singleton, # Provider type (Singleton, Factory, Resource)
)
class SomeProduct(Interface, Product):
Expand Down Expand Up @@ -230,7 +228,6 @@ Some planned features are:
- Enhance documentation and examples for better understanding
- Implement framework API and extension points for customization
- Improve injection resolution and initialization process
- Provide injection scopes and strategies for flexibility
- Testing framework integration for better test coverage
- Visualization tools for dependency graphs and relationships

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ testpaths = ["tests"]

[project]
name = "module_dependency"
version = "1.1.0"
version = "1.1.1"
dependencies = [
"dependency_injector",
"jinja2",
Expand Down
36 changes: 20 additions & 16 deletions src/dependency/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
from dependency.core.resolution import (
Container,
Registry,
InjectionResolver,
ResolutionConfig,
ResolutionStrategy,
)
from dependency.core.exceptions import (
DependencyError,
CancelInitialization,
)

from dependency.core.agrupation import (
Entrypoint,
Module,
Expand All @@ -13,18 +25,16 @@
instance,
providers,
)
from dependency.core.resolution import (
Container,
InjectionResolver,
ResolutionConfig,
ResolutionStrategy,
)
from dependency.core.exceptions import (
DependencyError,
CancelInitialization,
)


__all__ = [
"Container",
"Registry",
"InjectionResolver",
"ResolutionConfig",
"ResolutionStrategy",
"DependencyError",
"CancelInitialization",
"Entrypoint",
"Module",
"module",
Expand All @@ -36,10 +46,4 @@
"product",
"instance",
"providers",
"Container",
"InjectionResolver",
"ResolutionConfig",
"ResolutionStrategy",
"DependencyError",
"CancelInitialization",
]
22 changes: 9 additions & 13 deletions src/dependency/core/agrupation/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
from threading import Event
from typing import Iterable
from dependency.core.agrupation.plugin import Plugin
from dependency.core.injection.injectable import Injectable
from dependency.core.resolution.container import Container
from dependency.core.resolution.resolver import InjectionResolver
from dependency.core.resolution.strategy import ResolutionStrategy
_logger = logging.getLogger("dependency.loader")

class Entrypoint:
Expand All @@ -14,25 +14,21 @@ class Entrypoint:
Attributes:
init_time (float): Time when the entrypoint was initialized.
"""
init_time: float = time.time()

def __init__(self,
container: Container,
plugins: Iterable[type[Plugin]]
plugins: Iterable[type[Plugin]],
strategy: ResolutionStrategy = ResolutionStrategy()
) -> None:
providers: list[Injectable] = []

for plugin in plugins:
plugin.resolve_container(container=container)
providers.extend(plugin.resolve_providers())
init_time: float = time.time()

self.resolver: InjectionResolver = InjectionResolver(
container=container,
providers=providers
)

self.resolver.resolve_dependencies()
_logger.info(f"Application started in {time.time() - self.init_time} seconds")
self.resolver.resolve_dependencies(
modules=plugins,
strategy=strategy,
)
_logger.info(f"Application started in {time.time() - init_time} seconds")

def main_loop(self) -> None:
"""Main loop for the application. Waits indefinitely."""
Expand Down
7 changes: 6 additions & 1 deletion src/dependency/core/agrupation/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ class Plugin(Module):
def on_declaration(cls) -> None:
cls.injection.is_root = True

@classmethod
def on_resolution(cls,
container: Container
) -> None:
cls.resolve_container(container=container)

@classmethod
def resolve_container(cls, container: Container) -> None:
"""Resolve the plugin configuration.
Expand All @@ -43,7 +49,6 @@ def resolve_container(cls, container: Container) -> None:
ResolutionError: If the configuration is invalid.
"""
try:
cls.inject_container(container)
config_cls = get_type_hints(cls).get("config", object)
if issubclass(config_cls, BaseModel):
setattr(cls, "config", config_cls.model_validate(container.config()))
Expand Down
7 changes: 2 additions & 5 deletions src/dependency/core/declaration/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,15 @@ class Component(ProviderMixin):
def component(
module: Optional[type[Module]] = None,
imports: Iterable[type[ProviderMixin]] = (),
products: Iterable[type[ProviderMixin]] = (),
provider: Optional[InstanceOrClass[providers.Provider[Any]]] = None,
partial_resolution: bool = False,
provider: Optional[InstanceOrClass[providers.Provider[Any]]] = None,
bootstrap: bool = False,
) -> Callable[[type[COMPONENT]], type[COMPONENT]]:
"""Decorator for Component class

Args:
module (type[Module], optional): Module where the component is registered. Defaults to None.
imports (Iterable[type[ProviderMixin]], optional): List of components to be imported by the provider. Defaults to ().
products (Iterable[type[ProviderMixin]], optional): List of products to be declared by the provider. Defaults to ().
provider (Optional[providers.Provider[Any]], optional): Provider to be used. Defaults to None.
partial_resolution (bool, optional): Whether the component should be resolved with partial resolution. Defaults to False.
bootstrap (bool, optional): Whether the provider should be bootstrapped. Defaults to False.
Expand All @@ -49,9 +47,8 @@ def wrap(cls: type[COMPONENT]) -> type[COMPONENT]:
bootstrap=cls.provide if bootstrap else None,
)

cls.set_dependencies(
cls.update_dependencies(
imports=imports,
products=products,
partial_resolution=partial_resolution,
)

Expand Down
5 changes: 1 addition & 4 deletions src/dependency/core/declaration/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@

def instance(
imports: Iterable[type[ProviderMixin]] = (),
products: Iterable[type[ProviderMixin]] = (),
provider: type[providers.Provider[Any]] = providers.Singleton,
bootstrap: bool = False,
) -> Callable[[type[COMPONENT]], type[COMPONENT]]:
"""Decorator for instance class

Args:
imports (Iterable[type[ProviderMixin]], optional): List of components to be imported by the provider. Defaults to ().
products (Iterable[type[ProviderMixin]], optional): List of products to be declared by the provider. Defaults to ().
provider (type[providers.Provider[Any]], optional): Provider to be used. Defaults to providers.Singleton.
bootstrap (bool, optional): Whether the provider should be bootstrapped. Defaults to False.

Expand All @@ -34,9 +32,8 @@ def wrap(cls: type[COMPONENT]) -> type[COMPONENT]:
bootstrap=cls.provide if bootstrap else None,
)

cls.set_dependencies(
cls.update_dependencies(
imports=imports,
products=products,
)

return cls
Expand Down
2 changes: 0 additions & 2 deletions src/dependency/core/declaration/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ class Product(Component):
def product(
module: Optional[type[Module]] = None,
imports: Iterable[type[ProviderMixin]] = (),
products: Iterable[type[ProviderMixin]] = (),
provider: type[providers.Provider[Any]] = providers.Factory,
partial_resolution: bool = False,
bootstrap: bool = False,
Expand All @@ -35,7 +34,6 @@ def product(
return component(
module=module,
imports=imports,
products=products,
provider=provider,
partial_resolution=partial_resolution,
bootstrap=bootstrap,
Expand Down
Loading