Skip to content

Commit 697d3db

Browse files
authored
Merge pull request #1 from runemalm/feature/next-version
Feature/next version
2 parents c4db1cd + d617e84 commit 697d3db

File tree

13 files changed

+378
-118
lines changed

13 files changed

+378
-118
lines changed

.bumpversion.cfg

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[bumpversion]
2+
current_version = 1.0.0-alpha.2
3+
commit = False
4+
tag = False
5+
tag_name = v{new_version}
6+
message = Bump version: {current_version} → {new_version}
7+
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(-(?P<release>[^\+]+))?
8+
serialize =
9+
{major}.{minor}.{patch}-{release}
10+
11+
[bumpversion:file:setup.py]
12+
[bumpversion:file:docs/conf.py]

.github/actions/unittests/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Run unit tests
1+
name: Run unittests
22

33
inputs:
44
python-version:

.github/workflows/master.yml

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77

88
jobs:
99
unittests:
10-
name: Run tests
10+
name: Run unittests
1111
runs-on: ubuntu-latest
1212

1313
strategy:
@@ -23,3 +23,65 @@ jobs:
2323
uses: ./.github/actions/unittests
2424
with:
2525
python-version: ${{ matrix.python-version }}
26+
27+
prerelease:
28+
name: Pre-release (test-pypi)
29+
needs: unittests
30+
if: success()
31+
runs-on: ubuntu-latest
32+
33+
steps:
34+
- name: Build package
35+
run: python setup.py sdist bdist_wheel
36+
37+
- name: Upload artifact
38+
uses: actions/upload-artifact@v4
39+
with:
40+
name: py-dependency-injection
41+
path: dist/
42+
43+
- name: Publish to Test PyPI
44+
uses: pypa/gh-action-pypi-publish@releases/v1.8
45+
with:
46+
user: __token__
47+
password: ${{ secrets.TEST_PYPI_TOKEN }}
48+
repository_url: https://test.pypi.org/legacy/
49+
skip_existing: true
50+
51+
release:
52+
name: Release (pypi)
53+
needs: prerelease
54+
if: success() && (github.event_name == 'workflow_run' && github.event.workflow_run.event == 'workflow_dispatch')
55+
runs-on: ubuntu-latest
56+
57+
steps:
58+
- name: Download artifact
59+
uses: actions/download-artifact@v4
60+
with:
61+
name: py-dependency-injection
62+
path: dist/
63+
64+
- name: Extract version from setup.py
65+
id: extract-version
66+
run: |
67+
version=$(grep -Eo "version=['\"]([^'\"]+)['\"]" setup.py | awk -F"'" '{print $2}')
68+
echo "Using version: $version"
69+
echo "::set-output name=RELEASE_VERSION::$version"
70+
71+
- name: Publish to PyPI
72+
uses: pypa/gh-action-pypi-publish@releases/v1.8
73+
with:
74+
user: __token__
75+
password: ${{ secrets.PYPI_TOKEN }}
76+
repository_url: https://upload.pypi.org/legacy/
77+
skip_existing: true
78+
79+
- name: Set up Git
80+
run: |
81+
git config --global user.name "${{ github.actor }}"
82+
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
83+
84+
- name: Create and push tag
85+
run: |
86+
git tag -a v${{ steps.extract-version.outputs.RELEASE_VERSION }} -m "Release version v${{ steps.extract-version.outputs.RELEASE_VERSION }}"
87+
git push origin v${{ steps.extract-version.outputs.RELEASE_VERSION }}

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ clean: ## clean the build
4545
rm -rf build dist py_dependency_injection.egg-info
4646
find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete
4747

48+
.PHONY: bump_version
49+
bump_version: ## Bump the version
50+
pipenv run bump2version --dry-run release --allow-dirty --verbose
51+
4852
.PHONY: upload-test
4953
upload-test: ## upload package to testpypi repository
5054
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/*

Pipfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ sphinx-autobuild = "2021.3.14"
1313
sphinxcontrib-napoleon = "0.7"
1414
wheel = "0.41.2"
1515
pytest-xdist = "*"
16+
bump2version = "*"
1617

1718
[requires]
1819
python_version = ">=3.7"

Pipfile.lock

Lines changed: 20 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,77 +29,101 @@ $ pip install py-dependency-injection
2929

3030
The following examples demonstrates how to use the library.
3131

32-
### Example: Obtaining the Default Dependency Container
32+
### Obtaining the default dependency container
3333

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

3737
from dependency_injection.container import DependencyContainer
3838

39-
4039
dependency_container = DependencyContainer.get_instance()
4140
```
4241

43-
### Example: Obtaining a Second Dependency Container
42+
### Obtaining multiple dependency containers
4443

4544
```python
46-
# Create additional containers if needed, especially for multi-application scenarios.
45+
# Typically needed for multi-application scenarios.
4746

4847
from dependency_injection.container import DependencyContainer
4948

50-
51-
a_second_dependency_container = DependencyContainer.get_instance(name="a_second_dependency_container")
49+
second_container = DependencyContainer.get_instance(name="second_container")
50+
third_container = DependencyContainer.get_instance(name="third_container")
51+
# ...
5252
```
5353

54-
### Example: Registering Dependencies
54+
### Registering dependencies with the container
5555

5656
```python
57-
# Register dependencies with three available scopes: transient, scoped, or singleton.
57+
# Register dependencies using one of the three available scopes;
58+
# transient, scoped, or singleton
5859

5960
dependency_container.register_transient(SomeInterface, SomeClass)
6061
dependency_container.register_scoped(AnotherInterface, AnotherClass)
6162
dependency_container.register_singleton(ThirdInterface, ThirdClass)
6263
```
6364

64-
### Example: Resolving Dependencies
65+
### Resolving dependencies using the container
6566

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

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

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

77-
### Example: Constructor Injection
78+
### Constructor injection
7879

7980
```python
80-
# Class instances resolved through the container have dependencies injected into their constructors.
81+
# Class instances resolved through the container have
82+
# dependencies injected into their constructors automatically.
8183

8284
class Foo:
8385

84-
def __init__(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
86+
def __init__(
87+
self,
88+
transient_instance: SomeInterface,
89+
scoped_instance: AnotherInterface,
90+
singleton_instance: ThirdInterface
91+
):
8592
self._transient_instance = transient_instance
8693
self._scoped_instance = scoped_instance
8794
self._singleton_instance = singleton_instance
8895
```
8996

90-
### Example: Method Injection
97+
### Method injection with @inject decorator
9198

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

96103
from dependency_injection.decorator import inject
97104

98-
99105
class Foo:
100106

107+
# Class method
108+
@classmethod
109+
@inject()
110+
def bar_class(cls, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
111+
transient_instance.do_something()
112+
scoped_instance.do_something()
113+
singleton_instance.do_something()
114+
115+
# Static method
116+
@staticmethod
101117
@inject()
102-
def bar(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
118+
def bar_static_method(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
119+
transient_instance.do_something()
120+
scoped_instance.do_something()
121+
singleton_instance.do_something()
122+
123+
# Injecting with non-default container and scope
124+
@staticmethod
125+
@inject(container_name="second_container", scope_name="some_scope")
126+
def bar_with_decorator_arguments(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
103127
transient_instance.do_something()
104128
scoped_instance.do_something()
105129
singleton_instance.do_something()
@@ -115,6 +139,42 @@ To contribute, create a pull request on the develop branch following the [git fl
115139

116140
## Release Notes
117141

142+
### [1.0.0-alpha.3](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.3) (2024-03-02)
143+
144+
- **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.
145+
146+
**Reasoning:**
147+
148+
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.
149+
150+
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.
151+
152+
**Before:**
153+
```python
154+
class Foo:
155+
156+
@inject()
157+
def instance_method(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
158+
# ...
159+
```
160+
161+
**After:**
162+
```python
163+
class Foo:
164+
165+
@classmethod
166+
@inject()
167+
def class_method(cls, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
168+
# ...
169+
170+
@staticmethod
171+
@inject()
172+
def static_method(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
173+
# ...
174+
```
175+
176+
- 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.
177+
118178
### [1.0.0-alpha.2](https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.2) (2024-02-27)
119179

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

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
author = 'David Runemalm'
3232

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

3636

3737
# -- General configuration ---------------------------------------------------

docs/index.rst

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,6 @@ To get started quickly, take a look at the following example showcasing the basi
4646
4747
Explore the :doc:`user guide<gettingstarted>` to dive deeper into using the library effectively.
4848

49-
GitHub Repository
50-
=================
51-
52-
Find the source code, contribute, and report issues on our `GitHub Repository <https://github.com/runemalm/py-dependency-injection>`_.
53-
54-
Now, let's embark on a journey to harness the power of dependency injection with py-dependency-injection!
55-
5649
.. gettingstarted-docs:
5750
.. toctree::
5851
:maxdepth: 1

docs/versionhistory.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,42 @@
22
Version history
33
###############
44

5+
**1.0.0-alpha.3 (2024-03-02)**
6+
7+
- **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.
8+
9+
**Reasoning:**
10+
11+
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.
12+
13+
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.
14+
15+
**Before:**::
16+
17+
class Foo:
18+
19+
@inject()
20+
def instance_method(self, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
21+
# ...
22+
23+
**After:**::
24+
25+
class Foo:
26+
27+
@classmethod
28+
@inject()
29+
def class_method(cls, transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
30+
# ...
31+
32+
@staticmethod
33+
@inject()
34+
def static_method(transient_instance: SomeInterface, scoped_instance: AnotherInterface, singleton_instance: ThirdInterface):
35+
# ...
36+
37+
- 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.
38+
39+
`View release on GitHub <https://github.com/runemalm/py-dependency-injection/releases/tag/v1.0.0-alpha.3>`_
40+
541
**1.0.0-alpha.2 (2024-02-27)**
642

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

0 commit comments

Comments
 (0)