From eecac34a4e5fbaf0237f05d40e14fcb39d1881f7 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Mon, 25 Mar 2024 23:55:50 +0700 Subject: [PATCH 01/23] Refactor readme and documentation. --- README.md | 149 ++++++------------------- docs/conf.py | 2 +- docs/examples.rst | 239 ++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 29 ++--- docs/userguide.rst | 157 +------------------------- docs/versionhistory.rst | 6 +- 6 files changed, 292 insertions(+), 290 deletions(-) create mode 100644 docs/examples.rst diff --git a/README.md b/README.md index 37b19e1..705f24a 100644 --- a/README.md +++ b/README.md @@ -4,153 +4,72 @@ # py-dependency-injection -A dependency injection library for Python. +A simple yet powerful dependency injection library for Python. ## Features -- **Dependency Container:** Manage and resolve object dependencies with a flexible and easy-to-use container. -- **Dependency Scopes:** Define different scopes for dependencies, allowing for fine-grained control over their lifecycle. -- **Constructor Injection:** Inject dependencies into constructors, promoting cleaner and more modular code. -- **Method Injection:** Inject dependencies into methods, enabling more flexible dependency management within class instances. -- **Tags:** Register and resolve dependencies using tags, facilitating flexible and dynamic dependency management. -- **Factory Registration:** Register dependencies using factory functions for dynamic instantiation. -- **Instance Registration:** Register existing instances as dependencies, providing more control over object creation. -- **Python Compatibility:** Compatible with Python versions 3.7 to 3.12, ensuring broad compatibility with existing and future Python projects. +- **Scoped Registrations:** Define the lifetime of your dependencies as transient, scoped, or singleton. +- **Constructor Injection:** Automatically resolve and inject dependencies when creating instances. +- **Method Injection:** Inject dependencies into methods using a simple decorator. +- **Factory Functions:** Register factory functions, classes, or lambdas to create dependencies. +- **Instance Registration:** Register existing instances as dependencies. +- **Tag-based Registration and Resolution:** Organize and resolve dependencies based on tags. +- **Multiple Containers:** Support for using multiple dependency containers. ## Compatibility -This library is compatible with the following Python versions: - -- 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 +Compatible with Python versions 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12. ## Installation +Install py-dependency-injection using [pip](https://pip.pypa.io/en/stable/): + ```bash $ pip install py-dependency-injection ``` -## Basic Usage - -The following examples demonstrates how to use the library. - -### Creating a Dependency Container - -```python -# Get the default dependency container -dependency_container = DependencyContainer.get_instance() - -# Create additional named containers if needed -another_container = DependencyContainer.get_instance(name="another_container") -``` +## Quick Start -### Registering Dependencies with Scopes +Here's a quick example to get you started: ```python -# Register a transient dependency (a new instance every time) -dependency_container.register_transient(Connection, PostgresConnection) +from py_dependency_injection import DependencyContainer -# Register a scoped dependency (a new instance per scope) -dependency_container.register_scoped(Connection, PostgresConnection, scope_name="http_request") +# Define your dependencies and implementations +class Connection: + pass -# Register a singleton dependency (a single instance for the container's lifetime) -dependency_container.register_singleton(Connection, PostgresConnection) -``` +class PostgresConnection(Connection): + def __init__(self, host, port): + self.host = host + self.port = port -### Using Constructor Arguments +# Create a dependency container +container = DependencyContainer.get_instance() -```python -# Register a dependency with constructor arguments -dependency_container.register_transient( +# Register your dependencies +container.register_singleton( Connection, PostgresConnection, constructor_args={"host": "localhost", "port": 5432} ) -``` - -### Using Factory Functions - -```python -# Define a factory function -def create_connection(host: str, port: int) -> Connection: - return PostgresConnection(host=host, port=port) - -# Register the factory function -dependency_container.register_factory(Connection, create_connection, factory_args={"host": "localhost", "port": 5432}) -``` - -Besides functions, you can also use lambdas and class functions. Read more in the [documentation](https://py-dependency-injection.readthedocs.io/en/latest/userguide.html#using-factory-functions). - -### Registering and Using Instances - -```python -# Create an instance -my_connection = PostgresConnection(host="localhost", port=5432) - -# Register the instance -dependency_container.register_instance(Connection, my_connection) - -# Resolve the instance -resolved_connection = dependency_container.resolve(Connection) -print(resolved_connection.host) # Output: localhost -``` - -### Registering and Resolving with Tags -```python -# Register dependencies with tags -dependency_container.register_transient(Connection, PostgresConnection, tags={"Querying", "Startable"}) -dependency_container.register_scoped(BusConnection, KafkaBusConnection, tags={"Publishing", "Startable"}) - -# Resolve dependencies by tags -startable_dependencies = dependency_container.resolve_all(tags={"Startable"}) -for dependency in startable_dependencies: - dependency.start() +# Resolve and use your dependencies +connection = container.resolve(Connection) +print(connection.host) # Output: localhost ``` -### Using Constructor Injection - -```python -class OrderRepository: - def __init__(self, connection: Connection): - self.connection = connection - -# Register dependencies -dependency_container.register_transient(OrderRepository) -dependency_container.register_singleton(Connection, PostgresConnection) - -# Resolve the OrderRepository with injected dependencies -repository = dependency_container.resolve(OrderRepository) -print(repository.connection.__class__.__name__) # Output: PostgresConnection -``` - -### Using Method Injection - -```python -class OrderController: - @staticmethod - @inject() - def place_order(order: Order, repository: OrderRepository): - order.status = "placed" - repository.save(order) - -# Register the dependency -dependency_container.register_transient(OrderRepository) -dependency_container.register_singleton(Connection, PostgresConnection) - -# Use method injection to inject the dependency -my_order = Order.create() -OrderController.place_order(order=my_order) # The repository instance will be automatically injected -``` - -You can also specify container and scope using the decorator arguments `container_name` and `scope_name`. - ## Documentation -For the latest documentation, visit [readthedocs](https://py-dependency-injection.readthedocs.io/en/latest/). +For detailed documentation, including advanced usage and examples, please visit our [readthedocs](https://py-dependency-injection.readthedocs.io/en/latest/) page. ## Contribution -To contribute, create a pull request on the develop branch following the [git flow](https://nvie.com/posts/a-successful-git-branching-model/) branching model. +Contributions are welcome! Please feel free to submit pull requests or open issues to improve the library. + +## License + +`py-dependency-injection` is released under the GPL 3 license. See [LICENSE](LICENSE) for more details. ## Release Notes diff --git a/docs/conf.py b/docs/conf.py index bd61a2d..da511ec 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -84,6 +84,6 @@ html_static_path = ["_static"] intersphinx_mapping = { - "python": ("https://docs.python.org/", None), + "python": ("https://docs.python.org/3", None), "sqlalchemy": ("http://docs.sqlalchemy.org/en/latest/", None), } diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..64c809f --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,239 @@ +############################## +Creating dependency containers +############################## + +.. code-block:: python + + # Get the default dependency container + dependency_container = DependencyContainer.get_instance() + + # Get an additional container if necessary + another_container = DependencyContainer.get_instance( + name="another_container" + ) + + +#################################### +Registering dependencies with scopes +#################################### + +.. code-block:: python + + # Register a transient dependency + dependency_container.register_transient( + Connection, + PostgresConnection + ) + + # Register a scoped dependency + dependency_container.register_scoped( + Connection, + PostgresConnection, + scope_name="http_request" + ) + + # Register a singleton dependency + dependency_container.register_singleton( + Connection, + PostgresConnection + ) + + +###################################### +Registering with constructor arguments +###################################### + +.. code-block:: python + + # Register with constructor arguments + dependency_container.register_transient( + Connection, + PostgresConnection, + constructor_args={ + "host": "localhost", + "port": 5432 + } + ) + + +################################ +Resolving with factory functions +################################ + +Any `callable `_ can be used as factory function. + +.. code-block:: python + + # Define factory function + def factory_function(host: str, port: int) -> Connection: + return PostgresConnection( + host=host, + port=port + ) + + # Register with factory function + dependency_container.register_factory( + Connection, + factory_function, + factory_args={ + "host": "localhost", + "port": 5432 + } + ) + +.. code-block:: python + + # Define factory class + class FactoryClass: + @staticmethod + def create(host: str, port: int) -> Connection: + return PostgresConnection( + host=host, + port=port + ) + + # Register with factory class + dependency_container.register_factory( + Connection, + FactoryClass.create, + factory_args={ + "host": "localhost", + "port": 5432 + } + ) + +.. code-block:: python + + # Register with lambda factory function + dependency_container.register_factory( + Connection, + lambda host, port: PostgresConnection( + host=host, + port=port + ), + factory_args={ + "host": "localhost", + "port": 5432 + } + ) + + +############################### +Registering and using instances +############################### + +.. code-block:: python + + # Create instance + instance = PostgresConnection( + host="localhost", + port=5432 + ) + + # Register instance + dependency_container.register_instance( + Connection, + instance + ) + + # Resolve instance + resolved_instance = dependency_container.resolve(Connection) + print(resolved_instance.host) # Output: localhost + + +################################### +Registering and resolving with tags +################################### + +.. code-block:: python + + # Register with tags + dependency_container.register_scoped( + Connection, + PostgresConnection, + tags={ + Querying, + Startable + } + ) + + # Register another dependency with tags + dependency_container.register_scoped( + BusConnection, + KafkaBusConnection, + tags={ + Publishing, + Startable + } + ) + + # Resolve all dependencies with tag + tagged_dependencies = dependency_container.resolve_all( + tags={ + Startable + } + ) + + # Use resolved dependencies + for dependency in tagged_dependencies: + dependency.start() + + +########################### +Using constructor injection +########################### + +.. code-block:: python + + class OrderRepository: + def __init__(self, connection: Connection): + self.connection = connection + + # Register dependencies + dependency_container.register_transient( + OrderRepository + ) + + dependency_container.register_singleton( + Connection, + PostgresConnection + ) + + # Resolve with injected dependencies + repository = dependency_container.resolve( + OrderRepository + ) + + # Use injected dependency + print(repository.connection.__class__.__name__) # Output: PostgresConnection + + +###################### +Using method injection +###################### + +.. code-block:: python + + class OrderController: + @staticmethod + @inject() + def place_order(order: Order, repository: OrderRepository): + order.set_status("placed") + repository.save(order) + + # Register dependencies + dependency_container.register_transient( + OrderRepository + ) + + dependency_container.register_singleton( + Connection, + PostgresConnection + ) + + # Call decorated method (missing argument will be injected) + OrderController.place_order( + order=Order.create() + ) + +Pass decorator arguments ``container_name`` and ``scope_name`` if needed. diff --git a/docs/index.rst b/docs/index.rst index 403b345..168ef25 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,24 +1,19 @@ py-dependency-injection ======================= -Welcome to the documentation for the `py-dependency-injection` library. +Welcome to the `py-dependency-injection` library, a prototypical implementation designed to streamline dependency management in Python applications. This library provides a flexible and intuitive approach to dependency injection, enabling developers to maintain simple code and improve modularity. -Introduction -============ - -The `py-dependency-injection` library simplifies and enhances the management of dependencies in your Python projects. Whether you're new to the concept of dependency injection or an experienced developer seeking an efficient solution, this guide is designed to help you grasp the fundamentals and leverage the features offered by the library. Features -============ +======== -- **Dependency Container:** Manage and resolve object dependencies with a flexible and easy-to-use container. -- **Dependency Scopes:** Define different scopes for dependencies, allowing for fine-grained control over their lifecycle. -- **Constructor Injection:** Inject dependencies into constructors, promoting cleaner and more modular code. -- **Method Injection:** Inject dependencies into methods, enabling more flexible dependency management within class instances. -- **Tags:** Register and resolve dependencies using tags, facilitating flexible and dynamic dependency management. -- **Factory Registration:** Register dependencies using factory functions for dynamic instantiation. -- **Instance Registration:** Register existing instances as dependencies, providing more control over object creation. -- **Python Compatibility:** Compatible with Python versions 3.7 to 3.12, ensuring broad compatibility with existing and future Python projects. +- Dependency Container +- Dependency Scope +- Constructor Injection +- Method Injection +- Factory Registration +- Instance Registration +- Tags .. userguide-docs: .. toctree:: @@ -27,12 +22,12 @@ Features userguide -.. concepts-docs: +.. examples-docs: .. toctree:: :maxdepth: 1 - :caption: Basic Concepts + :caption: Examples - concepts + examples .. versionhistory-docs: .. toctree:: diff --git a/docs/userguide.rst b/docs/userguide.rst index 3407d8a..fc42c59 100644 --- a/docs/userguide.rst +++ b/docs/userguide.rst @@ -1,160 +1,7 @@ -############### -Getting Started -############### - - +############ Installation ------------- +############ Install using `pip `_:: $ pip install py-dependency-injection - - -Creating a Dependency Container -------------------------------- - -.. code-block:: python - - # Get the default dependency container - dependency_container = DependencyContainer.get_instance() - - # Create additional named containers if needed - another_container = DependencyContainer.get_instance(name="another_container") - - -Registering Dependencies with Scopes ------------------------------------- - -.. code-block:: python - - # Register a transient dependency (a new instance every time) - dependency_container.register_transient(Connection, PostgresConnection) - - # Register a scoped dependency (a new instance per scope) - dependency_container.register_scoped(Connection, PostgresConnection, scope_name="http_request") - - # Register a singleton dependency (a single instance for the container's lifetime) - dependency_container.register_singleton(Connection, PostgresConnection) - - -Using Constructor Arguments ---------------------------- - -.. code-block:: python - - # Register a dependency with constructor arguments - dependency_container.register_transient( - Connection, - PostgresConnection, - constructor_args={"host": "localhost", "port": 5432} - ) - - -Using Factory Functions ------------------------ - -.. code-block:: python - - # Define a factory function - def create_connection(host: str, port: int) -> Connection: - return PostgresConnection(host=host, port=port) - - # Register dependency with the factory function - dependency_container.register_factory(Connection, create_connection, factory_args={"host": "localhost", "port": 5432}) - -.. code-block:: python - - # Register dependency with a lambda factory function - dependency_container.register_factory( - Connection, - lambda host, port: PostgresConnection(host=host, port=port), - factory_args={"host": "localhost", "port": 5432} - ) - -.. code-block:: python - - # Register dependency with a factory class function - class ConnectionFactory: - @staticmethod - def create_connection(host: str, port: int) -> Connection: - return PostgresConnection(host=host, port=port) - - # Register the factory method - dependency_container.register_factory( - Connection, - connection_factory.create_connection, - factory_args={"host": "localhost", "port": 5432} - ) - - -Registering and Using Instances -------------------------------- - -.. code-block:: python - - # Create an instance - my_connection = PostgresConnection(host="localhost", port=5432) - - # Register the instance - dependency_container.register_instance(Connection, my_connection) - - # Resolve the instance - resolved_connection = dependency_container.resolve(Connection) - print(resolved_connection.host) # Output: localhost - - -Registering and Resolving with Tags ------------------------------------ - -.. code-block:: python - - # Register dependencies with tags - dependency_container.register_transient(Connection, PostgresConnection, tags={"Querying", "Startable"}) - dependency_container.register_scoped(BusConnection, KafkaBusConnection, tags={"Publishing", "Startable"}) - - # Resolve dependencies by tags - startable_dependencies = dependency_container.resolve_all(tags={"Startable"}) - for dependency in startable_dependencies: - dependency.start() - - -Using Constructor Injection ---------------------------- - -.. code-block:: python - - class OrderRepository: - def __init__(self, connection: Connection): - self.connection = connection - - # Register dependencies - dependency_container.register_transient(OrderRepository) - dependency_container.register_singleton(Connection, PostgresConnection) - - # Resolve the OrderRepository with injected dependencies - repository = dependency_container.resolve(OrderRepository) - print(repository.connection.__class__.__name__) # Output: PostgresConnection - - -Using Method Injection ----------------------- - -.. code-block:: python - - class OrderController: - @staticmethod - @inject() - def place_order(order: Order, repository: OrderRepository): - order.status = "placed" - repository.save(order) - - # Register the dependency - dependency_container.register_transient(OrderRepository) - dependency_container.register_singleton(Connection, PostgresConnection) - - # Use method injection to inject the dependency - my_order = Order.create() - OrderController.place_order(order=my_order) # The repository instance will be automatically injected - -You can also specify container and scope using the decorator arguments ``container_name`` and ``scope_name``. diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index ef33413..fbe7377 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -2,13 +2,15 @@ Version history ############### +.. warning:: + + This library is currently in the alpha stage of development. Expect changes and improvements as we work towards a stable release. + **1.0.0-alpha.7 (2024-03-24)** - Documentation Update: Updated the documentation to provide clearer instructions and more comprehensive examples. - Preparing for Beta Release: Made necessary adjustments and refinements in preparation for the upcoming first beta release. -This release focuses on enhancing the documentation and making final preparations for the transition to the beta phase. If you have any more updates or need further assistance, feel free to reach out! - `View release on GitHub `_ **1.0.0-alpha.6 (2024-03-23)** From ae2fa3a98445e81976b976203f94ddfdae77e07f Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Mon, 25 Mar 2024 23:57:13 +0700 Subject: [PATCH 02/23] Update readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 705f24a..43593c3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # py-dependency-injection -A simple yet powerful dependency injection library for Python. +A dependency injection library for Python. ## Features From f3543e53256b2f542bc5180679681f34616193ce Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Tue, 26 Mar 2024 10:04:28 +0700 Subject: [PATCH 03/23] Update README. --- README.md | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 43593c3..8e7d29b 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # py-dependency-injection -A dependency injection library for Python. +A simple dependency injection library for Python. ## Features @@ -18,12 +18,16 @@ A dependency injection library for Python. ## Compatibility -Compatible with Python versions 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12. +Compatible with the following Python versions: +- 3.7 +- 3.8 +- 3.9 +- 3.10 +- 3.11 +- 3.12 ## Installation -Install py-dependency-injection using [pip](https://pip.pypa.io/en/stable/): - ```bash $ pip install py-dependency-injection ``` @@ -35,38 +39,36 @@ Here's a quick example to get you started: ```python from py_dependency_injection import DependencyContainer -# Define your dependencies and implementations class Connection: pass class PostgresConnection(Connection): - def __init__(self, host, port): - self.host = host - self.port = port + pass + +class UserRepository: + def __init__(connection: Connection): + self._connection = connection -# Create a dependency container container = DependencyContainer.get_instance() -# Register your dependencies -container.register_singleton( - Connection, - PostgresConnection, - constructor_args={"host": "localhost", "port": 5432} -) +container.register_singleton(Connection, PostgresConnection) +container.register_transient(UserRepository) -# Resolve and use your dependencies -connection = container.resolve(Connection) -print(connection.host) # Output: localhost +user_repository = container.resolve(UserRepository) ``` ## Documentation -For detailed documentation, including advanced usage and examples, please visit our [readthedocs](https://py-dependency-injection.readthedocs.io/en/latest/) page. +For more advanced usage and examples, please visit our [readthedocs](https://py-dependency-injection.readthedocs.io/en/latest/) page. ## Contribution Contributions are welcome! Please feel free to submit pull requests or open issues to improve the library. +## Source Code + +You can find the source code for `py-dependency-injection` on [GitHub](https://github.com/runemalm/py-dependency-injection). Feel free to explore, contribute, or fork the repository. + ## License `py-dependency-injection` is released under the GPL 3 license. See [LICENSE](LICENSE) for more details. From 6b8a619b8eb73372bddef34075a7588652ece2fd Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Tue, 26 Mar 2024 11:56:47 +0700 Subject: [PATCH 04/23] Refactor README and documentation a bit more. --- README.md | 11 ++++------- docs/examples.rst | 6 ++++-- docs/index.rst | 27 ++++++++++++++------------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 8e7d29b..6626f06 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,13 @@ A simple dependency injection library for Python. - **Method Injection:** Inject dependencies into methods using a simple decorator. - **Factory Functions:** Register factory functions, classes, or lambdas to create dependencies. - **Instance Registration:** Register existing instances as dependencies. -- **Tag-based Registration and Resolution:** Organize and resolve dependencies based on tags. +- **Tag-Based Registration and Resolution:** Organize and resolve dependencies based on tags. - **Multiple Containers:** Support for using multiple dependency containers. ## Compatibility -Compatible with the following Python versions: +Compatible and tested with the following Python versions: + - 3.7 - 3.8 - 3.9 @@ -61,13 +62,9 @@ user_repository = container.resolve(UserRepository) For more advanced usage and examples, please visit our [readthedocs](https://py-dependency-injection.readthedocs.io/en/latest/) page. -## Contribution - -Contributions are welcome! Please feel free to submit pull requests or open issues to improve the library. - ## Source Code -You can find the source code for `py-dependency-injection` on [GitHub](https://github.com/runemalm/py-dependency-injection). Feel free to explore, contribute, or fork the repository. +You can find the source code for `py-dependency-injection` on [GitHub](https://github.com/runemalm/py-dependency-injection). ## License diff --git a/docs/examples.rst b/docs/examples.rst index 64c809f..85595c4 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -60,7 +60,8 @@ Registering with constructor arguments Resolving with factory functions ################################ -Any `callable `_ can be used as factory function. +.. note:: + Any `callable `_ can be used as factory function. .. code-block:: python @@ -236,4 +237,5 @@ Using method injection order=Order.create() ) -Pass decorator arguments ``container_name`` and ``scope_name`` if needed. +.. note:: + You can pass ``container_name`` and ``scope_name`` arguments to the ``@inject`` decorator to specify anything other than the default container and/or scope. diff --git a/docs/index.rst b/docs/index.rst index 168ef25..56453f4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,19 +1,25 @@ py-dependency-injection ======================= -Welcome to the `py-dependency-injection` library, a prototypical implementation designed to streamline dependency management in Python applications. This library provides a flexible and intuitive approach to dependency injection, enabling developers to maintain simple code and improve modularity. +A simple yet powerful dependency injection library for Python. +Introduction +------------ -Features -======== +Welcome to py-dependency-injection, a robust dependency injection library designed for Python applications. This library offers a straightforward approach to managing dependencies, enabling you to create more modular, testable, and maintainable code. It's well-suited for a variety of projects, from simple scripts to complex applications. -- Dependency Container -- Dependency Scope +py-dependency-injection provides essential features such as dependency scopes, constructor and method injection, factory and instance registration, and tag-based resolution. These tools allow you to decouple your application components effectively, making your codebase more organized and easier to manage. + +Key Features +------------ + +- Dependency Containers +- Dependency Scopes - Constructor Injection - Method Injection - Factory Registration - Instance Registration -- Tags +- Tag-Based Resolution .. userguide-docs: .. toctree:: @@ -36,16 +42,11 @@ Features versionhistory -.. community-docs: -.. toctree:: - :maxdepth: 1 - :caption: Community - - community - .. apireference-docs: .. toctree:: :maxdepth: 1 :caption: API Reference py-modindex + +You can find the source code at our `GitHub `_ repository. From 033e2f5f9c21374b37443efd36dde65830ae36a1 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Tue, 26 Mar 2024 11:57:58 +0700 Subject: [PATCH 05/23] Change README a bit. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6626f06..b1c555a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # py-dependency-injection -A simple dependency injection library for Python. +A simple yet powerful dependency injection library for Python. ## Features From 24592a9f0a820dcab4acc02d2f9f9ba0fff48d56 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Tue, 26 Mar 2024 12:06:16 +0700 Subject: [PATCH 06/23] Make more minor changes to readme and documentation. --- README.md | 12 +++++------- docs/index.rst | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b1c555a..99309e5 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A simple yet powerful dependency injection library for Python. ## Compatibility -Compatible and tested with the following Python versions: +Compatible with the following Python versions: - 3.7 - 3.8 @@ -62,14 +62,14 @@ user_repository = container.resolve(UserRepository) For more advanced usage and examples, please visit our [readthedocs](https://py-dependency-injection.readthedocs.io/en/latest/) page. -## Source Code - -You can find the source code for `py-dependency-injection` on [GitHub](https://github.com/runemalm/py-dependency-injection). - ## License `py-dependency-injection` is released under the GPL 3 license. See [LICENSE](LICENSE) for more details. +## Source Code + +You can find the source code for `py-dependency-injection` on [GitHub](https://github.com/runemalm/py-dependency-injection). + ## Release Notes ### [1.0.0-alpha.7](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.7) (2024-03-24) @@ -77,8 +77,6 @@ You can find the source code for `py-dependency-injection` on [GitHub](https://g - Documentation Update: Updated the documentation to provide clearer instructions and more comprehensive examples. - Preparing for Beta Release: Made necessary adjustments and refinements in preparation for the upcoming first beta release. -This release focuses on enhancing the documentation and making final preparations for the transition to the beta phase. If you have any more updates or need further assistance, feel free to reach out! - ### [1.0.0-alpha.6](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.6) (2024-03-23) - Factory Registration: Added support for registering dependencies using factory functions for dynamic instantiation. diff --git a/docs/index.rst b/docs/index.rst index 56453f4..b7ffd15 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,8 +10,8 @@ Welcome to py-dependency-injection, a robust dependency injection library design py-dependency-injection provides essential features such as dependency scopes, constructor and method injection, factory and instance registration, and tag-based resolution. These tools allow you to decouple your application components effectively, making your codebase more organized and easier to manage. -Key Features ------------- +Features +-------- - Dependency Containers - Dependency Scopes @@ -49,4 +49,4 @@ Key Features py-modindex -You can find the source code at our `GitHub `_ repository. +You can find the source code for `py-dependency-injection` on `GitHub `_. From 910e2b2e9d7d6fc882350c377df0c4de3fcf1dd0 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Tue, 26 Mar 2024 12:18:11 +0700 Subject: [PATCH 07/23] Update documentation some more. --- docs/examples.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index 85595c4..ae6b1b4 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -168,15 +168,15 @@ Registering and resolving with tags } ) - # Resolve all dependencies with tag - tagged_dependencies = dependency_container.resolve_all( + # Resolve all dependencies with a specific tag + resolved_dependencies = dependency_container.resolve_all( tags={ Startable } ) # Use resolved dependencies - for dependency in tagged_dependencies: + for dependency in resolved_dependencies: dependency.start() @@ -238,4 +238,4 @@ Using method injection ) .. note:: - You can pass ``container_name`` and ``scope_name`` arguments to the ``@inject`` decorator to specify anything other than the default container and/or scope. + You can pass ``container_name`` and ``scope_name`` arguments to the ``@inject`` decorator to specify container and/or scope. If none of the arguments are passed, the `default container` and the `default scope` will be used. From 16730e43e12750ea99fdb0f2aae0a0d340a4fcbc Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Tue, 26 Mar 2024 13:15:44 +0700 Subject: [PATCH 08/23] Update documentation some more. --- docs/index.rst | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index b7ffd15..3816220 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,14 +1,21 @@ py-dependency-injection ======================= -A simple yet powerful dependency injection library for Python. +A prototypical dependency injection library for Python. -Introduction ------------- +Purpose +------- -Welcome to py-dependency-injection, a robust dependency injection library designed for Python applications. This library offers a straightforward approach to managing dependencies, enabling you to create more modular, testable, and maintainable code. It's well-suited for a variety of projects, from simple scripts to complex applications. +Dependency injection is a powerful design pattern that promotes loose coupling and enhances testability in software applications. `py-dependency-injection` is a prototypical implementation of this pattern, designed to provide the essential features needed for effective dependency management in both small scripts and larger software projects. -py-dependency-injection provides essential features such as dependency scopes, constructor and method injection, factory and instance registration, and tag-based resolution. These tools allow you to decouple your application components effectively, making your codebase more organized and easier to manage. +This library is particularly suited for beginners exploring the concept of dependency injection, as it offers a straightforward and easy-to-understand implementation. It serves as an excellent starting point for learning the pattern and can also be used as a foundational base for frameworks requiring a more specialized interface for dependency injection. + +Key Advantages +-------------- + +- **Suitable for Learning:** Ideal for beginners exploring the concept of dependency injection. +- **Base Implementation for Frameworks:** Can be used as a foundational base for frameworks requiring a more specialized interface for dependency injection. +- **Standalone Solution:** Can be used on its own as a dependency injection solution in any software project. Features -------- @@ -21,6 +28,11 @@ Features - Instance Registration - Tag-Based Resolution +Documentation +------------- + +For a detailed guide on how to use `py-dependency-injection`, please refer to the examples. + .. userguide-docs: .. toctree:: :maxdepth: 1 From e6a2e5e04fc29d331e7f0d7cd4dce6bf5e0b4278 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Wed, 27 Mar 2024 12:00:15 +0700 Subject: [PATCH 09/23] Update documentation. --- docs/concepts.rst | 67 ----------------------- docs/examples.rst | 29 ++++++++-- docs/index.rst | 30 +++------- docs/py-modindex.rst | 2 +- docs/{versionhistory.rst => releases.rst} | 8 +-- docs/userguide.rst | 7 ++- 6 files changed, 43 insertions(+), 100 deletions(-) delete mode 100644 docs/concepts.rst rename docs/{versionhistory.rst => releases.rst} (99%) diff --git a/docs/concepts.rst b/docs/concepts.rst deleted file mode 100644 index 130c536..0000000 --- a/docs/concepts.rst +++ /dev/null @@ -1,67 +0,0 @@ -############## -Basic Concepts -############## - - -Dependency Injection --------------------- - -Dependency Injection (DI) is a design pattern that enables the inversion of control in software applications by allowing the injection of dependencies from external sources. In the context of `py-dependency-injection`, it simplifies the management of object dependencies and promotes modular and testable code. - - -Dependency Container --------------------- - -The Dependency Container is a central component that manages the registration and resolution of dependencies. It acts as a repository for holding instances of classes and their dependencies, facilitating the inversion of control provided by dependency injection. - - -Constructor Injection ---------------------- - -Constructor Injection is a form of dependency injection where dependencies are provided through a class's constructor. This pattern enhances code readability, maintainability, and testability by explicitly declaring and injecting dependencies when creating an object. - - -Method Injection ----------------- - -Method Injection is another form of dependency injection where dependencies are injected into an object's method rather than its constructor. This allows for more flexible and dynamic dependency management, as dependencies can be provided at the time of method invocation. - - -Factory Registration --------------------- - -Factory Registration is a technique where a factory function or class is used to create instances of a dependency. This allows for more complex instantiation logic, such as conditional creation based on runtime parameters or integration with external resources. - - -Instance Registration ---------------------- - -Instance Registration involves registering an already created instance of an object as a dependency. This is useful when you want to use a specific instance with a predefined state or when integrating with third-party libraries that provide instances of their classes. - - -Tags ----- - -Tags are used to categorize and identify dependencies within the container. By registering and resolving dependencies with tags, you can group related dependencies and retrieve them collectively. This is particularly useful in scenarios where you need to apply the same operation to multiple dependencies or when you want to resolve dependencies based on certain criteria. - - -Scoped Dependencies -------------------- - -Scoped Dependencies refer to instances of objects that have a limited scope during their lifecycle. In `py-dependency-injection`, you can register dependencies with three different scopes, which are transient, scoped, or singleton, allowing control over how instances are created and managed. - - -Dependency Scopes ------------------ - -Dependency Scopes define the lifecycle and visibility of a dependency within the application. The `py-dependency-injection` library supports three scopes: - -- **Transient**: A new instance is created each time the dependency is resolved. -- **Scoped**: A single instance is created within a specific scope (e.g., a request in a web application) and reused across that scope. -- **Singleton**: A single instance is created and shared throughout the application's lifetime. - - -Dependency Resolution ---------------------- - -Dependency Resolution is the process of retrieving an instance of a required dependency from the container. The `py-dependency-injection` library provides various methods for resolving dependencies, including direct resolution by type, resolution by tag, and resolution with constructor or method injection. diff --git a/docs/examples.rst b/docs/examples.rst index ae6b1b4..17d4801 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -2,6 +2,8 @@ Creating dependency containers ############################## +In this example, we demonstrate how to create and retrieve dependency containers using the `DependencyContainer` class. This is useful when you want to manage dependencies in different contexts or areas of your application. + .. code-block:: python # Get the default dependency container @@ -17,6 +19,8 @@ Creating dependency containers Registering dependencies with scopes #################################### +This example shows how to register dependencies with different scopes (transient, scoped, and singleton). This is important for controlling the lifecycle and reuse of your dependencies. + .. code-block:: python # Register a transient dependency @@ -43,6 +47,8 @@ Registering dependencies with scopes Registering with constructor arguments ###################################### +Here, we illustrate how to register a dependency with constructor arguments. This allows you to provide specific values or configurations to your dependencies when they are instantiated. + .. code-block:: python # Register with constructor arguments @@ -60,8 +66,10 @@ Registering with constructor arguments Resolving with factory functions ################################ +In this section, we demonstrate how to register and resolve dependencies using the factory pattern. This provides flexibility in how your dependencies are created and configured. You can use factory functions, factory classes and factory lambdas. + .. note:: - Any `callable `_ can be used as factory function. + Any `callable `_ can be used as factory. .. code-block:: python @@ -123,6 +131,8 @@ Resolving with factory functions Registering and using instances ############################### +This example demonstrates how to register and use instances of your dependencies. This is useful when you want to provide a specific instance of a dependency for use throughout your application. + .. code-block:: python # Create instance @@ -146,6 +156,8 @@ Registering and using instances Registering and resolving with tags ################################### +In this example, we show how to register and resolve dependencies using tags. This allows you to categorize and retrieve specific groups of dependencies based on their tags. + .. code-block:: python # Register with tags @@ -168,7 +180,7 @@ Registering and resolving with tags } ) - # Resolve all dependencies with a specific tag + # Resolve all dependencies with the 'Startable' tag resolved_dependencies = dependency_container.resolve_all( tags={ Startable @@ -184,6 +196,8 @@ Registering and resolving with tags Using constructor injection ########################### +This example illustrates how to use constructor injection to automatically inject dependencies into your classes. This is a common pattern for managing dependencies in object-oriented programming. This is probably how you'll want to resolve 99% of the dependencies in your software application. + .. code-block:: python class OrderRepository: @@ -213,6 +227,14 @@ Using constructor injection Using method injection ###################### +This example demonstrates how to use method injection to inject dependencies into methods at runtime. This is useful for dynamically providing dependencies to class- or static methods, without affecting the entire class. + +.. note:: + You can pass the arguments ``container_name`` and ``scope_name`` to ``@inject``. + +.. note:: + The ``@inject`` has to be applied to the function after the ``@classmethod`` or ``@staticmethod``. + .. code-block:: python class OrderController: @@ -236,6 +258,3 @@ Using method injection OrderController.place_order( order=Order.create() ) - -.. note:: - You can pass ``container_name`` and ``scope_name`` arguments to the ``@inject`` decorator to specify container and/or scope. If none of the arguments are passed, the `default container` and the `default scope` will be used. diff --git a/docs/index.rst b/docs/index.rst index 3816220..f7a75ec 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,3 +1,7 @@ +.. warning:: + + This library is currently in the alpha stage of development. Expect changes and improvements as we work towards a stable release. + py-dependency-injection ======================= @@ -15,28 +19,12 @@ Key Advantages - **Suitable for Learning:** Ideal for beginners exploring the concept of dependency injection. - **Base Implementation for Frameworks:** Can be used as a foundational base for frameworks requiring a more specialized interface for dependency injection. -- **Standalone Solution:** Can be used on its own as a dependency injection solution in any software project. - -Features --------- - -- Dependency Containers -- Dependency Scopes -- Constructor Injection -- Method Injection -- Factory Registration -- Instance Registration -- Tag-Based Resolution - -Documentation -------------- - -For a detailed guide on how to use `py-dependency-injection`, please refer to the examples. +- **Standalone Solution:** Can also be used on its own, as a fully-featured dependency injection solution in any software project. .. userguide-docs: .. toctree:: :maxdepth: 1 - :caption: User guide + :caption: User Guide userguide @@ -47,12 +35,12 @@ For a detailed guide on how to use `py-dependency-injection`, please refer to th examples -.. versionhistory-docs: +.. releases-docs: .. toctree:: :maxdepth: 1 :caption: Releases - versionhistory + releases .. apireference-docs: .. toctree:: @@ -61,4 +49,4 @@ For a detailed guide on how to use `py-dependency-injection`, please refer to th py-modindex -You can find the source code for `py-dependency-injection` on `GitHub `_. +You can find the source code for `py-dependency-injection` in our `GitHub repository `_. diff --git a/docs/py-modindex.rst b/docs/py-modindex.rst index 3cdb8ca..bcb43b4 100644 --- a/docs/py-modindex.rst +++ b/docs/py-modindex.rst @@ -1,2 +1,2 @@ -API reference +API Reference ============= diff --git a/docs/versionhistory.rst b/docs/releases.rst similarity index 99% rename from docs/versionhistory.rst rename to docs/releases.rst index fbe7377..4af61df 100644 --- a/docs/versionhistory.rst +++ b/docs/releases.rst @@ -1,11 +1,11 @@ -############### -Version history -############### - .. warning:: This library is currently in the alpha stage of development. Expect changes and improvements as we work towards a stable release. +############### +Version History +############### + **1.0.0-alpha.7 (2024-03-24)** - Documentation Update: Updated the documentation to provide clearer instructions and more comprehensive examples. diff --git a/docs/userguide.rst b/docs/userguide.rst index fc42c59..0463dd5 100644 --- a/docs/userguide.rst +++ b/docs/userguide.rst @@ -1,6 +1,9 @@ -############ +############### +Getting Started +############### + Installation -############ +--------------- Install using `pip `_:: From c3a144998579ebba0d09c52a2be387ba3ce48778 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Sun, 31 Mar 2024 15:21:08 +0700 Subject: [PATCH 10/23] Change library description in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99309e5..22b3d03 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # py-dependency-injection -A simple yet powerful dependency injection library for Python. +A prototypical dependency injection library for Python. ## Features From c4aa61e77647bd00d6cc86a93073279c8f87b373 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Sun, 31 Mar 2024 15:21:35 +0700 Subject: [PATCH 11/23] Rename title in makefile. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 664b00d..84ff1ed 100644 --- a/Makefile +++ b/Makefile @@ -74,16 +74,16 @@ sphinx-autobuild: ## activate autobuild of docs pipenv run sphinx-autobuild docs docs/_build/html --watch $(SRC) ################################################################################ -# FORMAT & LINT +# PRE-COMMIT HOOKS ################################################################################ .PHONY: black black: ## run black auto-formatting - pipenv run black $(SRC) $(TESTS) --line-length=88 + pipenv run black $(SRC) $(TESTS) .PHONY: black-check black-check: ## check code don't violate black formatting rules - pipenv run black --check $(SRC) --line-length=88 + pipenv run black --check $(SRC) .PHONY: flake flake: ## lint code with flake From a4cee58c3263321d4e5e5fb850f3011ce2b34da0 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Sun, 31 Mar 2024 15:21:45 +0700 Subject: [PATCH 12/23] Add a test. --- .../resolve/test_resolve_with_alias.py | 22 +++++++++++++++++++ tests/unit_test/container/resolve/vehicle.py | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 tests/unit_test/container/resolve/test_resolve_with_alias.py create mode 100644 tests/unit_test/container/resolve/vehicle.py diff --git a/tests/unit_test/container/resolve/test_resolve_with_alias.py b/tests/unit_test/container/resolve/test_resolve_with_alias.py new file mode 100644 index 0000000..b1cbbbf --- /dev/null +++ b/tests/unit_test/container/resolve/test_resolve_with_alias.py @@ -0,0 +1,22 @@ +from dependency_injection.container import DependencyContainer + +from unit_test.unit_test_case import UnitTestCase + +from unit_test.container.resolve.vehicle import Vehicle +from unit_test.container.resolve.vehicle import Vehicle as VehicleAlias + + +class TestResolveWithAlias(UnitTestCase): + def test_register_with_alias_and_resolve_with_original_name( + self, + ): + # arrange + dependency_container = DependencyContainer.get_instance() + dependency_container.register_transient(VehicleAlias) + + # act + resolved_dependency = dependency_container.resolve(Vehicle) + + # assert + self.assertIsNotNone(resolved_dependency) + self.assertIsInstance(resolved_dependency, Vehicle) diff --git a/tests/unit_test/container/resolve/vehicle.py b/tests/unit_test/container/resolve/vehicle.py new file mode 100644 index 0000000..9a0bd64 --- /dev/null +++ b/tests/unit_test/container/resolve/vehicle.py @@ -0,0 +1,2 @@ +class Vehicle: + pass From fe32b7f5615d21cf9eaf2c9a1f2afefd0b82b9ff Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 31 May 2024 13:47:20 +0200 Subject: [PATCH 13/23] Make some makefile changes. --- Makefile | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 84ff1ed..a7b533d 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ HOME := $(shell echo ~) PWD := $(shell pwd) SRC := $(PWD)/src TESTS := $(PWD)/tests +DOCS := $(PWD)/docs # Load env file include env.make @@ -29,7 +30,7 @@ help: .PHONY: test test: ## run test suite - PYTHONPATH=./src:./tests pipenv run pytest -n 1 $(TESTS) + PYTHONPATH=$(SRC):$(TESTS) pipenv run pytest $(TESTS) ################################################################################ # RELEASE @@ -42,8 +43,10 @@ build: ## build the python package .PHONY: clean clean: ## clean the build python setup.py clean - rm -rf build dist py_dependency_injection.egg-info - find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete + rm -rf build dist + find . -type f -name '*.py[co]' -delete + find . -type d -name __pycache__ -exec rm -rf {} + + find . -type d -name '*.egg-info' -exec rm -rf {} + .PHONY: bump_version bump_version: ## Bump the version @@ -67,11 +70,13 @@ sphinx-html: ## build the sphinx html .PHONY: sphinx-rebuild sphinx-rebuild: ## re-build the sphinx docs - make -C docs clean && make -C docs html + cd $(DOCS) && \ + pipenv run make clean && pipenv run make html .PHONY: sphinx-autobuild sphinx-autobuild: ## activate autobuild of docs - pipenv run sphinx-autobuild docs docs/_build/html --watch $(SRC) + cd $(DOCS) && \ + pipenv run sphinx-autobuild . _build/html --watch $(SRC) ################################################################################ # PRE-COMMIT HOOKS @@ -105,12 +110,12 @@ pre-commit-run: ## run the pre-commit hooks pipenv-install: ## setup the virtual environment pipenv install --dev -.PHONY: pipenv-packages-install -pipenv-packages-install: ## install a package (uses PACKAGE) +.PHONY: pipenv-install-package +pipenv-install-package: ## install a package (uses PACKAGE) pipenv install $(PACKAGE) -.PHONY: pipenv-packages-install-dev -pipenv-packages-install-dev: ## install a dev package (uses PACKAGE) +.PHONY: pipenv-install-package-dev +pipenv-install-package-dev: ## install a dev package (uses PACKAGE) pipenv install --dev $(PACKAGE) .PHONY: pipenv-packages-graph @@ -121,12 +126,12 @@ pipenv-packages-graph: ## Check installed packages pipenv-requirements-generate: ## Check a requirements.txt pipenv lock -r > requirements.txt -.PHONY: pipenv-venv-activate -pipenv-venv-activate: ## Activate the virtual environment +.PHONY: pipenv-shell +pipenv-shell: ## Activate the virtual environment pipenv shell -.PHONY: pipenv-venv-path -pipenv-venv-path: ## Show the path to the venv +.PHONY: pipenv-venv +pipenv-venv: ## Show the path to the venv pipenv --venv .PHONY: pipenv-lock-and-install From 877e05560e68a0eeac75f5e36311fb2d79fef9d2 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 31 May 2024 13:48:09 +0200 Subject: [PATCH 14/23] Change README a bit. --- README.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 22b3d03..9cc241f 100644 --- a/README.md +++ b/README.md @@ -18,14 +18,9 @@ A prototypical dependency injection library for Python. ## Compatibility -Compatible with the following Python versions: +The library is compatible with the following Python versions: -- 3.7 -- 3.8 -- 3.9 -- 3.10 -- 3.11 -- 3.12 +- 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 ## Installation @@ -38,24 +33,40 @@ $ pip install py-dependency-injection Here's a quick example to get you started: ```python -from py_dependency_injection import DependencyContainer +from dependency_injection.container import DependencyContainer +# Define an abstract Connection class Connection: pass +# Define a specific implementation of the Connection class PostgresConnection(Connection): - pass + def connect(self): + print("Connecting to PostgreSQL database...") +# Define a repository that depends on some type of Connection class UserRepository: - def __init__(connection: Connection): + def __init__(self, connection: Connection): self._connection = connection + def fetch_users(self): + self._connection.connect() + print("Fetching users from the database...") + +# Get an instance of the (default) DependencyContainer container = DependencyContainer.get_instance() +# Register the specific connection type as a singleton instance container.register_singleton(Connection, PostgresConnection) + +# Register UserRepository as a transient (new instance every time) container.register_transient(UserRepository) +# Resolve an instance of UserRepository, automatically injecting the required Connection user_repository = container.resolve(UserRepository) + +# Use the resolved user_repository to perform an operation +user_repository.find_all() ``` ## Documentation From 73c8ce1e739f9fb9e7ad2c351ea4012f4f6acb39 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 31 May 2024 13:53:34 +0200 Subject: [PATCH 15/23] Update README a bit more. --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 9cc241f..6e090b6 100644 --- a/README.md +++ b/README.md @@ -35,16 +35,15 @@ Here's a quick example to get you started: ```python from dependency_injection.container import DependencyContainer -# Define an abstract Connection +# Define dependency abstraction class Connection: pass -# Define a specific implementation of the Connection +# Define dependency implementations class PostgresConnection(Connection): def connect(self): print("Connecting to PostgreSQL database...") -# Define a repository that depends on some type of Connection class UserRepository: def __init__(self, connection: Connection): self._connection = connection @@ -53,19 +52,17 @@ class UserRepository: self._connection.connect() print("Fetching users from the database...") -# Get an instance of the (default) DependencyContainer +# Get a dependency container container = DependencyContainer.get_instance() -# Register the specific connection type as a singleton instance +# Register dependencies container.register_singleton(Connection, PostgresConnection) - -# Register UserRepository as a transient (new instance every time) container.register_transient(UserRepository) -# Resolve an instance of UserRepository, automatically injecting the required Connection +# Resolve dependencies user_repository = container.resolve(UserRepository) -# Use the resolved user_repository to perform an operation +# Use the dependencies user_repository.find_all() ``` From 61be767cde1c28d060d973f3cf048c8814cf018f Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 31 May 2024 13:58:45 +0200 Subject: [PATCH 16/23] Update README a bit. --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6e090b6..9cc241f 100644 --- a/README.md +++ b/README.md @@ -35,15 +35,16 @@ Here's a quick example to get you started: ```python from dependency_injection.container import DependencyContainer -# Define dependency abstraction +# Define an abstract Connection class Connection: pass -# Define dependency implementations +# Define a specific implementation of the Connection class PostgresConnection(Connection): def connect(self): print("Connecting to PostgreSQL database...") +# Define a repository that depends on some type of Connection class UserRepository: def __init__(self, connection: Connection): self._connection = connection @@ -52,17 +53,19 @@ class UserRepository: self._connection.connect() print("Fetching users from the database...") -# Get a dependency container +# Get an instance of the (default) DependencyContainer container = DependencyContainer.get_instance() -# Register dependencies +# Register the specific connection type as a singleton instance container.register_singleton(Connection, PostgresConnection) + +# Register UserRepository as a transient (new instance every time) container.register_transient(UserRepository) -# Resolve dependencies +# Resolve an instance of UserRepository, automatically injecting the required Connection user_repository = container.resolve(UserRepository) -# Use the dependencies +# Use the resolved user_repository to perform an operation user_repository.find_all() ``` From faf8afdf3dea4d343a436741ae53d4c2027b62e5 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:40:19 +0200 Subject: [PATCH 17/23] Make minor documentation change. --- docs/examples.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/examples.rst b/docs/examples.rst index 17d4801..1346e04 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -6,6 +6,8 @@ In this example, we demonstrate how to create and retrieve dependency containers .. code-block:: python + from dependency_injection.container import DependencyContainer + # Get the default dependency container dependency_container = DependencyContainer.get_instance() From 621218c27009c0ec6a478a240f45b043835ba336 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:41:44 +0200 Subject: [PATCH 18/23] Fix bug where resolving with partial constructor arguments and auto-injected dependencies failed. --- src/dependency_injection/container.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/dependency_injection/container.py b/src/dependency_injection/container.py index 589954b..92abb02 100644 --- a/src/dependency_injection/container.py +++ b/src/dependency_injection/container.py @@ -153,19 +153,6 @@ def _validate_constructor_args( ) -> None: constructor = inspect.signature(implementation.__init__).parameters - # Check if any required parameter is missing - missing_params = [ - param - for param in constructor.keys() - if param not in ["self", "cls", "args", "kwargs"] - and param not in constructor_args - ] - if missing_params: - raise ValueError( - f"Missing required constructor arguments: " - f"{', '.join(missing_params)} for class '{implementation.__name__}'." - ) - for arg_name, arg_value in constructor_args.items(): if arg_name not in constructor: raise ValueError( From 8daff90f656c6a65ae1e37e199ce6efbd56a0e8d Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:43:48 +0200 Subject: [PATCH 19/23] Remove test. --- .../resolve/test_resolve_with_args.py | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/tests/unit_test/container/resolve/test_resolve_with_args.py b/tests/unit_test/container/resolve/test_resolve_with_args.py index 277d7e7..9883434 100644 --- a/tests/unit_test/container/resolve/test_resolve_with_args.py +++ b/tests/unit_test/container/resolve/test_resolve_with_args.py @@ -62,34 +62,6 @@ def __init__(self, color: str, make: str): ): dependency_container.resolve(dependency) - def test_resolve_with_missing_constructor_arg_raises( - self, - ): - # arrange - class Vehicle: - pass - - class Car(Vehicle): - def __init__(self, color: str, make: str): - self.color = color - self.make = make - - dependency_container = DependencyContainer.get_instance() - dependency = Vehicle - implementation = Car - dependency_container.register_transient( - dependency=dependency, - implementation=implementation, - constructor_args={"color": "red"}, - ) - - # act - with pytest.raises( - ValueError, - match="Missing required constructor arguments: make for class 'Car'.", - ): - dependency_container.resolve(dependency) - def test_resolve_with_wrong_constructor_arg_type_raises( self, ): From 0ba2bb29ee8d3ac530d9b039d8fc2b71e9d89cd7 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:44:24 +0200 Subject: [PATCH 20/23] Add tests for merging constructor args and auto-injected dependencies. --- .../resolve/test_resolve_with_args.py | 36 +++++++++++++++++++ .../resolve/test_resolve_with_injection.py | 27 ++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/unit_test/container/resolve/test_resolve_with_injection.py diff --git a/tests/unit_test/container/resolve/test_resolve_with_args.py b/tests/unit_test/container/resolve/test_resolve_with_args.py index 9883434..8894cb5 100644 --- a/tests/unit_test/container/resolve/test_resolve_with_args.py +++ b/tests/unit_test/container/resolve/test_resolve_with_args.py @@ -114,3 +114,39 @@ def __init__(self, color: str, make): # act + assert (no exception) dependency_container.resolve(dependency) + + def test_resolve_merges_registered_constructor_args_with_auto_injected_dependencies( + self, + ): + # arrange + class Engine: + pass + + class Vehicle: + pass + + class Car(Vehicle): + def __init__(self, color: str, engine: Engine): + self.color = color + self.engine = engine + + class CarFactory: + @classmethod + def create(cls, color: str, engine: Engine) -> Car: + return Car(color=color, engine=engine) + + dependency_container = DependencyContainer.get_instance() + dependency_container.register_transient(Engine) + dependency_container.register_transient( + Vehicle, + Car, + constructor_args={"color": "red"}, + ) + + # act + resolved_dependency = dependency_container.resolve(Vehicle) + + # assert + self.assertIsInstance(resolved_dependency, Car) + self.assertEqual("red", resolved_dependency.color) + self.assertIsInstance(resolved_dependency.engine, Engine) diff --git a/tests/unit_test/container/resolve/test_resolve_with_injection.py b/tests/unit_test/container/resolve/test_resolve_with_injection.py new file mode 100644 index 0000000..ba5f08e --- /dev/null +++ b/tests/unit_test/container/resolve/test_resolve_with_injection.py @@ -0,0 +1,27 @@ +from dependency_injection.container import DependencyContainer +from unit_test.unit_test_case import UnitTestCase + + +class TestResolveWithInjection(UnitTestCase): + def test_resolve_injects_dependencies_in_constructor( + self, + ): + # arrange + class Engine: + pass + + class Car: + def __init__(self, engine: Engine): + self.engine = engine + + dependency_container = DependencyContainer.get_instance() + dependency_container.register_transient(Engine) + dependency_container.register_transient(Car) + + # act + resolved_dependency = dependency_container.resolve(Car) + + # assert + self.assertIsInstance(resolved_dependency, Car) + self.assertIsNotNone(resolved_dependency.engine) + self.assertIsInstance(resolved_dependency.engine, Engine) From 888b39517fd70de07a589275d852f7fd4b0e5326 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:50:47 +0200 Subject: [PATCH 21/23] Add release note and bump version. --- README.md | 4 ++++ docs/conf.py | 2 +- docs/releases.rst | 6 ++++++ setup.py | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9cc241f..5659367 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,10 @@ You can find the source code for `py-dependency-injection` on [GitHub](https://g ## Release Notes +### [1.0.0-alpha.8](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.8) (2024-06-07) + +- Bug Fix: Fixed an issue in the dependency resolution logic where registered constructor arguments were not properly merged with automatically injected dependencies. This ensures that constructor arguments specified during registration are correctly combined with dependencies resolved by the container. + ### [1.0.0-alpha.7](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.7) (2024-03-24) - Documentation Update: Updated the documentation to provide clearer instructions and more comprehensive examples. diff --git a/docs/conf.py b/docs/conf.py index da511ec..e8e7018 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,7 @@ version = "1.0" # The full version, including alpha/beta/rc tags -release = "1.0.0-alpha.7" +release = "1.0.0-alpha.8" # -- General configuration --------------------------------------------------- diff --git a/docs/releases.rst b/docs/releases.rst index 4af61df..7ced55e 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -6,6 +6,12 @@ Version History ############### +**1.0.0-alpha.8 (2024-06-07)** + +- Bug Fix: Fixed an issue in the dependency resolution logic where registered constructor arguments were not properly merged with automatically injected dependencies. This ensures that constructor arguments specified during registration are correctly combined with dependencies resolved by the container. + +`View release on GitHub `_ + **1.0.0-alpha.7 (2024-03-24)** - Documentation Update: Updated the documentation to provide clearer instructions and more comprehensive examples. diff --git a/setup.py b/setup.py index 1ea2320..0ee8878 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="py-dependency-injection", - version="1.0.0-alpha.7", + version="1.0.0-alpha.8", author="David Runemalm, 2024", author_email="david.runemalm@gmail.com", description="A dependency injection library for Python.", From 6e9f759fe411e88d51e2bdaca7a0566acc0daf51 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:51:00 +0200 Subject: [PATCH 22/23] Change library description in pypi setup. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0ee8878..119345d 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ version="1.0.0-alpha.8", author="David Runemalm, 2024", author_email="david.runemalm@gmail.com", - description="A dependency injection library for Python.", + description="A prototypical dependency injection library for Python.", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/runemalm/py-dependency-injection", From 2f2f1537ae9b7c493feff582f36d6384d8039ff3 Mon Sep 17 00:00:00 2001 From: David Runemalm Date: Fri, 7 Jun 2024 21:58:13 +0200 Subject: [PATCH 23/23] Change release notes in doc a bit. --- docs/releases.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releases.rst b/docs/releases.rst index 7ced55e..b52e84f 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -8,7 +8,7 @@ Version History **1.0.0-alpha.8 (2024-06-07)** -- Bug Fix: Fixed an issue in the dependency resolution logic where registered constructor arguments were not properly merged with automatically injected dependencies. This ensures that constructor arguments specified during registration are correctly combined with dependencies resolved by the container. +- **Bug Fix**: Fixed an issue in the dependency resolution logic where registered constructor arguments were not properly merged with automatically injected dependencies. This ensures that constructor arguments specified during registration are correctly combined with dependencies resolved by the container. `View release on GitHub `_