Skip to content
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

Feature/next version #1

Merged
merged 9 commits into from
Mar 2, 2024
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
12 changes: 12 additions & 0 deletions .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[bumpversion]
current_version = 1.0.0-alpha.2
commit = False
tag = False
tag_name = v{new_version}
message = Bump version: {current_version} → {new_version}
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-(?P<release>[^\+]+))?
serialize =
{major}.{minor}.{patch}-{release}

[bumpversion:file:setup.py]
[bumpversion:file:docs/conf.py]
2 changes: 1 addition & 1 deletion .github/actions/unittests/action.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run unit tests
name: Run unittests

inputs:
python-version:
Expand Down
64 changes: 63 additions & 1 deletion .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
unittests:
name: Run tests
name: Run unittests
runs-on: ubuntu-latest

strategy:
Expand All @@ -23,3 +23,65 @@ jobs:
uses: ./.github/actions/unittests
with:
python-version: ${{ matrix.python-version }}

prerelease:
name: Pre-release (test-pypi)
needs: unittests
if: success()
runs-on: ubuntu-latest

steps:
- name: Build package
run: python setup.py sdist bdist_wheel

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: py-dependency-injection
path: dist/

- name: Publish to Test PyPI
uses: pypa/gh-action-pypi-publish@releases/v1.8
with:
user: __token__
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository_url: https://test.pypi.org/legacy/
skip_existing: true

release:
name: Release (pypi)
needs: prerelease
if: success() && (github.event_name == 'workflow_run' && github.event.workflow_run.event == 'workflow_dispatch')
runs-on: ubuntu-latest

steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: py-dependency-injection
path: dist/

- name: Extract version from setup.py
id: extract-version
run: |
version=$(grep -Eo "version=['\"]([^'\"]+)['\"]" setup.py | awk -F"'" '{print $2}')
echo "Using version: $version"
echo "::set-output name=RELEASE_VERSION::$version"

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@releases/v1.8
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}
repository_url: https://upload.pypi.org/legacy/
skip_existing: true

- name: Set up Git
run: |
git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com"

- name: Create and push tag
run: |
git tag -a v${{ steps.extract-version.outputs.RELEASE_VERSION }} -m "Release version v${{ steps.extract-version.outputs.RELEASE_VERSION }}"
git push origin v${{ steps.extract-version.outputs.RELEASE_VERSION }}
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ clean: ## clean the build
rm -rf build dist py_dependency_injection.egg-info
find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete

.PHONY: bump_version
bump_version: ## Bump the version
pipenv run bump2version --dry-run release --allow-dirty --verbose

.PHONY: upload-test
upload-test: ## upload package to testpypi repository
TWINE_USERNAME=$(PYPI_USERNAME_TEST) TWINE_PASSWORD=$(PYPI_PASSWORD_TEST) pipenv run twine upload --repository testpypi --skip-existing --repository-url https://test.pypi.org/legacy/ dist/*
Expand Down
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ sphinx-autobuild = "2021.3.14"
sphinxcontrib-napoleon = "0.7"
wheel = "0.41.2"
pytest-xdist = "*"
bump2version = "*"

[requires]
python_version = ">=3.7"
23 changes: 20 additions & 3 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

100 changes: 80 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,77 +29,101 @@ $ pip install py-dependency-injection

The following examples demonstrates how to use the library.

### Example: Obtaining the Default Dependency Container
### Obtaining the default dependency container

```python
# Retrieve the default container, typically recommended for a single-application setup.
# Typically all you need for a single-application setup.

from dependency_injection.container import DependencyContainer


dependency_container = DependencyContainer.get_instance()
```

### Example: Obtaining a Second Dependency Container
### Obtaining multiple dependency containers

```python
# Create additional containers if needed, especially for multi-application scenarios.
# Typically needed for multi-application scenarios.

from dependency_injection.container import DependencyContainer


a_second_dependency_container = DependencyContainer.get_instance(name="a_second_dependency_container")
second_container = DependencyContainer.get_instance(name="second_container")
third_container = DependencyContainer.get_instance(name="third_container")
# ...
```

### Example: Registering Dependencies
### Registering dependencies with the container

```python
# Register dependencies with three available scopes: transient, scoped, or singleton.
# Register dependencies using one of the three available scopes;
# transient, scoped, or singleton

dependency_container.register_transient(SomeInterface, SomeClass)
dependency_container.register_scoped(AnotherInterface, AnotherClass)
dependency_container.register_singleton(ThirdInterface, ThirdClass)
```

### Example: Resolving Dependencies
### Resolving dependencies using the container

```python
# Resolve transient instance (created anew for each call).
transient_instance = dependency_container.resolve(SomeInterface)

# Resolve scoped instance (consistent within a specific scope, e.g. a scope for the application action being run).
scoped_instance = dependency_container.resolve(AnotherInterface, scope_name="application_action_scope")
# Resolve scoped instance (consistent within a specific scope).
scoped_instance = dependency_container.resolve(AnotherInterface, scope_name="some_scope")

# Resolve singleton instance (consistent across the entire application).
singleton_instance = dependency_container.resolve(ThirdInterface)
```

### Example: Constructor Injection
### Constructor injection

```python
# Class instances resolved through the container have dependencies injected into their constructors.
# Class instances resolved through the container have
# dependencies injected into their constructors automatically.

class Foo:

def __init__(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
def __init__(
self,
transient_instance: SomeInterface,
scoped_instance: AnotherInterface,
singleton_instance: ThirdInterface
):
self._transient_instance = transient_instance
self._scoped_instance = scoped_instance
self._singleton_instance = singleton_instance
```

### Example: Method Injection
### Method injection with @inject decorator

```python
# Inject dependencies into instance methods using the `@inject` decorator.
# You may pass 'container_name' and 'scope_name' as decorator arguments.
# The decorator can be applied to classmethods and staticmethods.
# Instance method injection is not allowed.

from dependency_injection.decorator import inject


class Foo:

# Class method
@classmethod
@inject()
def bar_class(cls, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
transient_instance.do_something()
scoped_instance.do_something()
singleton_instance.do_something()

# Static method
@staticmethod
@inject()
def bar(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
def bar_static_method(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
transient_instance.do_something()
scoped_instance.do_something()
singleton_instance.do_something()

# Injecting with non-default container and scope
@staticmethod
@inject(container_name="second_container", scope_name="some_scope")
def bar_with_decorator_arguments(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
transient_instance.do_something()
scoped_instance.do_something()
singleton_instance.do_something()
Expand All @@ -115,6 +139,42 @@ To contribute, create a pull request on the develop branch following the [git fl

## Release Notes

### [1.0.0-alpha.3](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.3) (2024-03-02)

- **Breaking Change**: Restriction on `@inject` Decorator: Starting from this version, the `@inject` decorator can now only be used on static class methods and class methods. This change is introduced due to potential pitfalls associated with resolving and injecting dependencies directly into class instance methods using the dependency container.

**Reasoning:**

Resolving and injecting dependencies into instance methods can lead to unexpected behaviors and may violate the principles of dependency injection. Instance methods often rely on the state of the object, and injecting dependencies from the container directly can obscure the dependencies required for a method. Additionally, it may introduce difficulties in testing and make the code harder to reason about.

By restricting the usage of the `@inject` decorator to static and class methods, we aim to encourage a cleaner separation of concerns, making it more explicit when dependencies are injected and providing better clarity on the dependencies required by a method.

**Before:**
```python
class Foo:

@inject()
def instance_method(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
# ...
```

**After:**
```python
class Foo:

@classmethod
@inject()
def class_method(cls, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
# ...

@staticmethod
@inject()
def static_method(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
# ...
```

- Documentation Update: The documentation has been updated to reflect the new restriction on the usage of the `@inject` decorator. Users are advised to review the documentation for updated examples and guidelines regarding method injection.

### [1.0.0-alpha.2](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.2) (2024-02-27)

- Python Version Support: Added support for Python versions 3.7, 3.9, 3.10, 3.11, and 3.12.
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
author = 'David Runemalm'

# The full version, including alpha/beta/rc tags
release = '1.0.0-alpha.2'
release = '1.0.0-alpha.3'


# -- General configuration ---------------------------------------------------
Expand Down
7 changes: 0 additions & 7 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,6 @@ To get started quickly, take a look at the following example showcasing the basi

Explore the :doc:`user guide<gettingstarted>` to dive deeper into using the library effectively.

GitHub Repository
=================

Find the source code, contribute, and report issues on our `GitHub Repository <https://github.com/runemalm/py-dependency-injection>`_.

Now, let's embark on a journey to harness the power of dependency injection with py-dependency-injection!

.. gettingstarted-docs:
.. toctree::
:maxdepth: 1
Expand Down
36 changes: 36 additions & 0 deletions docs/versionhistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,42 @@
Version history
###############

**1.0.0-alpha.3 (2024-03-02)**

- **Breaking Change**: Restriction on `@inject` Decorator: Starting from this version, the `@inject` decorator can now only be used on static class methods and class methods. This change is introduced due to potential pitfalls associated with resolving and injecting dependencies directly into class instance methods using the dependency container.

**Reasoning:**

Resolving and injecting dependencies into instance methods can lead to unexpected behaviors and may violate the principles of dependency injection. Instance methods often rely on the state of the object, and injecting dependencies from the container directly can obscure the dependencies required for a method. Additionally, it may introduce difficulties in testing and make the code harder to reason about.

By restricting the usage of the `@inject` decorator to static and class methods, we aim to encourage a cleaner separation of concerns, making it more explicit when dependencies are injected and providing better clarity on the dependencies required by a method.

**Before:**::

class Foo:

@inject()
def instance_method(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
# ...

**After:**::

class Foo:

@classmethod
@inject()
def class_method(cls, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
# ...

@staticmethod
@inject()
def static_method(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
# ...

- Documentation Update: The documentation has been updated to reflect the new restriction on the usage of the `@inject` decorator. Users are advised to review the documentation for updated examples and guidelines regarding method injection.

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

**1.0.0-alpha.2 (2024-02-27)**

- Python Version Support: Added support for Python versions 3.7, 3.9, 3.10, 3.11, and 3.12.
Expand Down
Loading
Loading