Skip to content

Feature/next version #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 6, 2025
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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ pre-commit-run: ## run the pre-commit hooks
# PIPENV
################################################################################

.PHONY: pipenv-rm
pipenv-rm: ## remove the virtual environment
pipenv --rm

.PHONY: pipenv-install
pipenv-install: ## setup the virtual environment
pipenv install --dev
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ You can find the source code for `py-dependency-injection` on [GitHub](https://g

## Release Notes

### [1.0.0-beta.1](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-beta.1) (2025-01-06)

- **Transition to Beta**: Transitioned from alpha to beta. Features have been stabilized and are ready for broader testing.
- **Removal**: We have removed the dependency container getter and setter functions, as well as the RegistrationSerializer class, which were first introduced in v1.0.0-alpha.9. This decision reflects our focus on maintaining a streamlined library that emphasizes core functionality. These features, which would not be widely used, added unnecessary complexity without offering significant value. By removing them, we are reinforcing our commitment to our design principles.
- **Enhancement**: Added suppprt for configuring default scope name. Either a static string value, or a callable that returns the name.

### [1.0.0-alpha.10](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.10) (2024-08-11)

- **Tagged Constructor Injection**: Introduced support for constructor injection using the `Tagged`, `AnyTagged`, and `AllTagged` classes. This allows for seamless injection of dependencies that have been registered with specific tags, enhancing flexibility and control in managing your application's dependencies.
Expand Down
9 changes: 0 additions & 9 deletions docs/community.rst

This file was deleted.

2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
version = "1.0"

# The full version, including alpha/beta/rc tags
release = "1.0.0-alpha.10"
release = "1.0.0-beta.1"


# -- General configuration ---------------------------------------------------
Expand Down
12 changes: 3 additions & 9 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.. warning::
.. note::

This library is currently in the alpha stage of development. Expect changes and improvements as we work towards a stable release.
This library is currently in the beta stage of development.
While features are stable, some minor adjustments may occur before the final release.

py-dependency-injection
=======================
Expand Down Expand Up @@ -42,11 +43,4 @@ Key Advantages

releases

.. apireference-docs:
.. toctree::
:maxdepth: 1
:caption: API Reference

py-modindex

You can find the source code for `py-dependency-injection` in our `GitHub repository <https://github.com/runemalm/py-dependency-injection>`_.
9 changes: 0 additions & 9 deletions docs/modules/container.rst

This file was deleted.

9 changes: 0 additions & 9 deletions docs/modules/decorator.rst

This file was deleted.

9 changes: 0 additions & 9 deletions docs/modules/scope.rst

This file was deleted.

2 changes: 0 additions & 2 deletions docs/py-modindex.rst

This file was deleted.

13 changes: 11 additions & 2 deletions docs/releases.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
.. warning::
.. note::

This library is currently in the alpha stage of development. Expect changes and improvements as we work towards a stable release.
This library is currently in the beta stage of development.
While features are stable, some minor adjustments may occur before the final release.

###############
Version History
###############

**1.0.0-beta.1 (2025-01-06)**

- **Transition to Beta**: Transitioned from alpha to beta. Features have been stabilized and are ready for broader testing.
- **Removal**: We have removed the dependency container getter and setter functions, as well as the RegistrationSerializer class, which were first introduced in v1.0.0-alpha.9. This decision reflects our focus on maintaining a streamlined library that emphasizes core functionality. These features, which would not be widely used, added unnecessary complexity without offering significant value. By removing them, we are reinforcing our commitment to our design principles.
- **Enhancement**: Added suppprt for configuring default scope name. Either a static string value, or a callable that returns the name.

`View release on GitHub <https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-beta.1>`_

**1.0.0-alpha.10 (2024-08-11)**

- **Tagged Constructor Injection**: Introduced support for constructor injection using the `Tagged`, `AnyTagged`, and `AllTagged` classes. This allows for seamless injection of dependencies that have been registered with specific tags, enhancing flexibility and control in managing your application's dependencies.
Expand Down
86 changes: 84 additions & 2 deletions docs/userguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,91 @@
Getting Started
###############

.. note::
`py-dependency-injection` has transitioned to beta! Features are now stable and ready for broader testing. Feedback is encouraged to help refine the library for its final release.

############
Introduction
############

`py-dependency-injection` is a lightweight and flexible dependency injection library for Python. It simplifies managing dependencies in your applications, promoting cleaner and more testable code.

This guide will help you understand the key concepts and how to start using the library. For detailed examples, see the `Examples` section.

############
Installation
---------------
############

Install using `pip <http://pypi.python.org/pypi/pip/>`_::
Install the library using pip:

.. code-block:: bash

$ pip install py-dependency-injection

The library supports Python versions 3.7 through 3.12.

##########################
Core Concepts and Features
##########################

`py-dependency-injection` offers:

- **Scoped Dependency Management**: Define lifetimes for your dependencies (e.g., transient, scoped, singleton).
- **Flexible Registrations**: Use constructors, factories, or predefined instances for dependency registration.
- **Tag-Based Organization**: Categorize and resolve dependencies using tags.
- **Multiple Containers**: Isolate dependencies for different parts of your application.

Refer to the `Examples` section for detailed usage scenarios.

####################
Quick Start Overview
####################

1. **Create a Dependency Container**:
- The `DependencyContainer` is the core object for managing dependencies.

2. **Register Dependencies**:
- Dependencies can be registered with different lifetimes: transient, scoped, or singleton.

3. **Resolve Dependencies**:
- Use the container to resolve dependencies where needed.

Basic workflow:

.. code-block:: python

from dependency_injection.container import DependencyContainer

class Connection:
pass

class PostgresConnection(Connection):
pass

# Create a container
container = DependencyContainer.get_instance()

# Register and resolve dependencies
container.register_singleton(Connection, PostgresConnection)
connection = container.resolve(Connection)
print(type(connection).__name__) # Output: PostgresConnection

##############
Best Practices
##############

- **Use Constructor Injection**: Preferred for most cases as it promotes clear and testable designs.
- **Leverage Tags for Organization**: Group dependencies logically using tags.
- **Choose the Right Scope**: Use scoped or singleton lifetimes to optimize performance and resource usage.
- **Keep Dependencies Decoupled**: Avoid tightly coupling your components to the container.
- **Isolate Contexts with Containers**: Use multiple containers to manage dependencies for separate modules or contexts.

#################
Where to Go Next?
#################

- **Examples**:
Explore detailed examples of how to register, resolve, and manage dependencies effectively in the `Examples` section.

- **Community and Support**:
Join our community on GitHub to ask questions, report issues, or contribute to the project.
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name="py-dependency-injection",
version="1.0.0-alpha.10",
version="1.0.0-beta.1",
author="David Runemalm, 2024",
author_email="[email protected]",
description="A dependency injection library for Python.",
Expand Down Expand Up @@ -34,7 +34,7 @@
],
python_requires=">=3.7",
classifiers=[
"Development Status :: 3 - Alpha",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Programming Language :: Python :: 3.7",
Expand Down
40 changes: 26 additions & 14 deletions src/dependency_injection/container.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import inspect
from dataclasses import is_dataclass

from typing import Any, Callable, Dict, List, Optional, TypeVar, Type
from typing import Any, Callable, Dict, List, Optional, TypeVar, Type, Union

from dependency_injection.tags.all_tagged import AllTagged
from dependency_injection.tags.any_tagged import AnyTagged
Expand All @@ -17,13 +17,29 @@


class DependencyContainer(metaclass=SingletonMeta):
_default_scope_name: Union[str, Callable[[], str]] = DEFAULT_SCOPE_NAME

def __init__(self, name: str = None):
self.name = name if name is not None else DEFAULT_CONTAINER_NAME
self._registrations = {}
self._singleton_instances = {}
self._scoped_instances = {}
self._has_resolved = False

@classmethod
def configure_default_scope_name(
cls, default_scope_name: Union[str, Callable[[], str]]
) -> None:
"""Configure the global default scope name, which can be string or callable."""
cls._default_scope_name = default_scope_name

@classmethod
def get_default_scope_name(cls) -> str:
"""Return the default scope name. If it's callable, call it to get the value."""
if callable(cls._default_scope_name):
return cls._default_scope_name()
return cls._default_scope_name

@classmethod
def get_instance(cls, name: str = None) -> Self:
name = name or DEFAULT_CONTAINER_NAME
Expand All @@ -33,16 +49,6 @@ def get_instance(cls, name: str = None) -> Self:

return cls._instances[(cls, name)]

def get_registrations(self) -> Dict[Type, Registration]:
return self._registrations

def set_registrations(self, registrations) -> None:
if self._has_resolved:
raise Exception(
"You can't set registrations after a dependency has been resolved."
)
self._registrations = registrations

def register_transient(
self,
dependency: Type,
Expand Down Expand Up @@ -109,8 +115,9 @@ def _register(
dependency, implementation, scope, tags, constructor_args
)

def resolve(self, dependency: Type, scope_name: str = DEFAULT_SCOPE_NAME) -> Any:
def resolve(self, dependency: Type, scope_name: Optional[str] = None) -> Any:
self._has_resolved = True
scope_name = scope_name or self.get_default_scope_name()

if scope_name not in self._scoped_instances:
self._scoped_instances[scope_name] = {}
Expand All @@ -124,8 +131,11 @@ def resolve(self, dependency: Type, scope_name: str = DEFAULT_SCOPE_NAME) -> Any

return self._resolve_by_scope(registration, scope_name)

def _resolve_by_scope(self, registration: Registration, scope_name: str) -> Any:
def _resolve_by_scope(
self, registration: Registration, scope_name: Optional[str] = None
) -> Any:
scope = registration.scope
scope_name = scope_name or self.get_default_scope_name()

if scope == Scope.TRANSIENT:
return self._inject_dependencies(
Expand Down Expand Up @@ -210,9 +220,11 @@ def _validate_registration(self, dependency: Type) -> None:
def _inject_dependencies(
self,
implementation: Type,
scope_name: str = None,
scope_name: Optional[str] = None,
constructor_args: Optional[Dict[str, Any]] = None,
) -> Type:
scope_name = scope_name or self.get_default_scope_name()

if is_dataclass(implementation):
return implementation() # Do not inject into dataclasses

Expand Down
8 changes: 4 additions & 4 deletions src/dependency_injection/decorator.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import functools
import inspect
from typing import Any, Callable, TypeVar
from typing import Any, Callable, Optional, TypeVar

from dependency_injection.container import DEFAULT_CONTAINER_NAME, DependencyContainer
from dependency_injection.scope import DEFAULT_SCOPE_NAME

F = TypeVar("F", bound=Callable[..., Any])


def inject(
container_name=DEFAULT_CONTAINER_NAME, scope_name=DEFAULT_SCOPE_NAME
container_name=DEFAULT_CONTAINER_NAME, scope_name: Optional[str] = None
) -> Callable[[F], F]:
def is_instance_method(func: Callable[..., Any]) -> bool:
parameters = inspect.signature(func).parameters
Expand All @@ -30,10 +29,11 @@ def wrapper_inject(*args: Any, **kwargs: Any) -> Any:
if parameter_name != "cls" and parameter_name not in kwargs:
# get container
container = DependencyContainer.get_instance(container_name)
actual_scope_name = scope_name or container.get_default_scope_name()
# Resolve the dependency based on the parameter name
dependency_type = sig.parameters[parameter_name].annotation
kwargs[parameter_name] = container.resolve(
dependency_type, scope_name=scope_name
dependency_type, scope_name=actual_scope_name
)

# Call the original function with the injected dependencies
Expand Down
15 changes: 0 additions & 15 deletions src/dependency_injection/serialization.py

This file was deleted.

Loading
Loading