dependency-injection-pattern 0.0.5__tar.gz → 0.0.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dependency_injection_pattern-0.0.5/src/dependency_injection_pattern.egg-info → dependency_injection_pattern-0.0.7}/PKG-INFO +125 -13
- dependency_injection_pattern-0.0.7/README.md +182 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/pyproject.toml +3 -2
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7/src/dependency_injection_pattern.egg-info}/PKG-INFO +125 -13
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/dependency_injection_pattern.egg-info/requires.txt +1 -1
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/container.py +17 -2
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/default_scope.py +1 -1
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_provider.py +20 -0
- dependency_injection_pattern-0.0.5/README.md +0 -71
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/LICENSE +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/setup.cfg +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/dependency_injection_pattern.egg-info/SOURCES.txt +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/dependency_injection_pattern.egg-info/dependency_links.txt +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/dependency_injection_pattern.egg-info/top_level.txt +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/__init__.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/exceptions/add_exception.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/exceptions/container_sealed_error.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/exceptions/factory_provide_exception.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/exceptions/implementation_exception.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/exceptions/provide_exception.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/exceptions/seal_exception.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factories/dependency.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factories/factory.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factories/scoped_factory.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factories/singleton_factory.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factories/transient_factory.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factory.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/helpers.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/log.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/named_service.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/protocols/container.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/protocols/factory.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/protocols/provider.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/provider.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/scope.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/service_request.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/exceptions.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_container.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_examples.py +0 -0
- {dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_factory.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dependency-injection-pattern
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: Python implementation of the Dependency Injection pattern.
|
|
5
5
|
Author-email: Anders Madsen <anders.madsen@alphavue.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,15 +18,16 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Programming Language :: Python
|
|
22
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
24
|
Classifier: Topic :: Software Development :: Libraries
|
|
24
25
|
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python:
|
|
26
|
+
Requires-Python: <3.15,>=3.10
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
License-File: LICENSE
|
|
28
29
|
Requires-Dist: typing-utilities<0.2,>=0.1.0
|
|
29
|
-
Requires-Dist: runtime-reflection-lite<0.
|
|
30
|
+
Requires-Dist: runtime-reflection-lite<0.3,>=0.2.0
|
|
30
31
|
Provides-Extra: test
|
|
31
32
|
Requires-Dist: pytest>=8.3; extra == "test"
|
|
32
33
|
Requires-Dist: pytest-cov>=6.1; extra == "test"
|
|
@@ -39,22 +40,133 @@ Dynamic: license-file
|
|
|
39
40
|

|
|
40
41
|
[](https://pepy.tech/projects/dependency-injection-pattern)
|
|
41
42
|
|
|
42
|
-
#
|
|
43
|
-
> A Python implementation of the Dependency Injection pattern.
|
|
43
|
+
# Dependency Injection Pattern (Python)
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
A typed dependency injection framework for Python — focused on clarity, structure, and maintainability.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
## 🚀 Why this exists
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
When a service is requested, the provider looks it up in the factory dictionary, which then gets or creates a fitting instance. Any parameters needed for instantiation is supplied (injected) by the provider.
|
|
49
|
+
As Python applications grow, managing dependencies becomes increasingly difficult:
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
- Hidden dependencies make systems harder to understand
|
|
52
|
+
- Tight coupling reduces flexibility
|
|
53
|
+
- Testing requires complex setup and mocking
|
|
54
|
+
- Structure becomes inconsistent across modules
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
Unlike many other ecosystems, Python lacks a widely adopted, structured approach to dependency injection.
|
|
57
|
+
|
|
58
|
+
👉 This project aims to introduce **clear, typed dependency management** to Python applications.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## ✨ Features
|
|
62
|
+
|
|
63
|
+
- 🧩 Strongly typed dependency injection
|
|
64
|
+
- 🧠 Explicit dependency definitions
|
|
65
|
+
- 🔄 Clear separation of concerns
|
|
66
|
+
- 🧪 Improved testability
|
|
67
|
+
- 🏗 Support for structured application architecture
|
|
68
|
+
- ⚡ Lightweight and Python-native design
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## 📦 Use cases
|
|
72
|
+
|
|
73
|
+
This library is particularly useful when building:
|
|
74
|
+
|
|
75
|
+
- Backend services
|
|
76
|
+
- Data engineering workflows
|
|
77
|
+
- ETL pipelines
|
|
78
|
+
- Modular applications
|
|
79
|
+
- Systems with complex dependency graphs
|
|
80
|
+
|
|
81
|
+
👉 Especially valuable in projects where maintainability and scalability matter.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## 🏗 Design philosophy
|
|
85
|
+
|
|
86
|
+
This project is built around a few key principles:
|
|
87
|
+
|
|
88
|
+
### 1. Explicit over implicit
|
|
89
|
+
Dependencies should be visible and well-defined — not hidden in implementation details.
|
|
90
|
+
|
|
91
|
+
### 2. Type-driven structure
|
|
92
|
+
Type hints are used as a foundation for defining and resolving dependencies.
|
|
93
|
+
|
|
94
|
+
### 3. Maintainability over convenience
|
|
95
|
+
The goal is not to reduce lines of code — but to make systems easier to evolve.
|
|
96
|
+
|
|
97
|
+
### 4. Minimal abstraction
|
|
98
|
+
The framework avoids unnecessary complexity and stays close to Python’s core concepts.
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
## 🔗 What this enables
|
|
102
|
+
|
|
103
|
+
By introducing structured dependency injection, you get:
|
|
104
|
+
|
|
105
|
+
- Clear boundaries between components
|
|
106
|
+
- Easier reasoning about system design
|
|
107
|
+
- More predictable runtime behavior
|
|
108
|
+
- Improved ability to refactor safely
|
|
109
|
+
|
|
110
|
+
👉 This is especially powerful in larger systems where complexity grows over time.
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## ⚖️ Trade-offs
|
|
114
|
+
|
|
115
|
+
| Focus ✅ | Not a goal ❌ |
|
|
116
|
+
|---------|--------------|
|
|
117
|
+
| Structured architecture | Magic-based auto-wiring |
|
|
118
|
+
| Type safety | Implicit dependency resolution |
|
|
119
|
+
| Clarity and control | Minimal configuration at all costs |
|
|
120
|
+
|
|
121
|
+
👉 This library prioritizes **explicit design over automation**.
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## 🧠 Inspiration
|
|
125
|
+
|
|
126
|
+
Inspired by dependency injection patterns from ecosystems such as:
|
|
127
|
+
|
|
128
|
+
- C# / .NET (built-in DI container)
|
|
129
|
+
- Java (Spring, Guice)
|
|
130
|
+
|
|
131
|
+
But intentionally adapted to fit Python’s dynamic nature
|
|
132
|
+
without introducing heavy frameworks or unnecessary indirection.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## 🎯 When to use
|
|
136
|
+
|
|
137
|
+
Use this library if you:
|
|
138
|
+
|
|
139
|
+
- Are building medium to large Python applications
|
|
140
|
+
- Want clearer separation between components
|
|
141
|
+
- Need better testability
|
|
142
|
+
- Prefer structured architecture over ad-hoc patterns
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
## 🚫 When not to use
|
|
146
|
+
|
|
147
|
+
This library may be unnecessary if:
|
|
148
|
+
|
|
149
|
+
- Your project is small or short-lived
|
|
150
|
+
- Dependency complexity is minimal
|
|
151
|
+
- Simplicity is more important than structure
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## 🔗 Related projects
|
|
155
|
+
|
|
156
|
+
Part of a broader focus on:
|
|
157
|
+
|
|
158
|
+
- Clean architecture
|
|
159
|
+
- Runtime abstractions
|
|
160
|
+
- Developer tooling in Python
|
|
161
|
+
|
|
162
|
+
👉 https://github.com/apmadsen
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
## 🤝 Contributing
|
|
167
|
+
|
|
168
|
+
Feedback, ideas, and contributions are welcome!
|
|
56
169
|
|
|
57
|
-
- Singleton services may not depend on scoped or transient services, as this would negate the "transient" or "scoped" part since a singleton service would keep a reference to it's dependencies indefinitely.
|
|
58
170
|
|
|
59
171
|
## Example:
|
|
60
172
|
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
[](https://github.com/apmadsen/dependency-injection-pattern/actions/workflows/python-test.yml)
|
|
2
|
+
[](https://github.com/apmadsen/dependency-injection-pattern/actions/workflows/python-test-coverage.yml)
|
|
3
|
+
[](https://github.com/apmadsen/dependency-injection-pattern/releases)
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
[](https://pepy.tech/projects/dependency-injection-pattern)
|
|
7
|
+
|
|
8
|
+
# Dependency Injection Pattern (Python)
|
|
9
|
+
|
|
10
|
+
A typed dependency injection framework for Python — focused on clarity, structure, and maintainability.
|
|
11
|
+
|
|
12
|
+
## 🚀 Why this exists
|
|
13
|
+
|
|
14
|
+
As Python applications grow, managing dependencies becomes increasingly difficult:
|
|
15
|
+
|
|
16
|
+
- Hidden dependencies make systems harder to understand
|
|
17
|
+
- Tight coupling reduces flexibility
|
|
18
|
+
- Testing requires complex setup and mocking
|
|
19
|
+
- Structure becomes inconsistent across modules
|
|
20
|
+
|
|
21
|
+
Unlike many other ecosystems, Python lacks a widely adopted, structured approach to dependency injection.
|
|
22
|
+
|
|
23
|
+
👉 This project aims to introduce **clear, typed dependency management** to Python applications.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## ✨ Features
|
|
27
|
+
|
|
28
|
+
- 🧩 Strongly typed dependency injection
|
|
29
|
+
- 🧠 Explicit dependency definitions
|
|
30
|
+
- 🔄 Clear separation of concerns
|
|
31
|
+
- 🧪 Improved testability
|
|
32
|
+
- 🏗 Support for structured application architecture
|
|
33
|
+
- ⚡ Lightweight and Python-native design
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## 📦 Use cases
|
|
37
|
+
|
|
38
|
+
This library is particularly useful when building:
|
|
39
|
+
|
|
40
|
+
- Backend services
|
|
41
|
+
- Data engineering workflows
|
|
42
|
+
- ETL pipelines
|
|
43
|
+
- Modular applications
|
|
44
|
+
- Systems with complex dependency graphs
|
|
45
|
+
|
|
46
|
+
👉 Especially valuable in projects where maintainability and scalability matter.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
## 🏗 Design philosophy
|
|
50
|
+
|
|
51
|
+
This project is built around a few key principles:
|
|
52
|
+
|
|
53
|
+
### 1. Explicit over implicit
|
|
54
|
+
Dependencies should be visible and well-defined — not hidden in implementation details.
|
|
55
|
+
|
|
56
|
+
### 2. Type-driven structure
|
|
57
|
+
Type hints are used as a foundation for defining and resolving dependencies.
|
|
58
|
+
|
|
59
|
+
### 3. Maintainability over convenience
|
|
60
|
+
The goal is not to reduce lines of code — but to make systems easier to evolve.
|
|
61
|
+
|
|
62
|
+
### 4. Minimal abstraction
|
|
63
|
+
The framework avoids unnecessary complexity and stays close to Python’s core concepts.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## 🔗 What this enables
|
|
67
|
+
|
|
68
|
+
By introducing structured dependency injection, you get:
|
|
69
|
+
|
|
70
|
+
- Clear boundaries between components
|
|
71
|
+
- Easier reasoning about system design
|
|
72
|
+
- More predictable runtime behavior
|
|
73
|
+
- Improved ability to refactor safely
|
|
74
|
+
|
|
75
|
+
👉 This is especially powerful in larger systems where complexity grows over time.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## ⚖️ Trade-offs
|
|
79
|
+
|
|
80
|
+
| Focus ✅ | Not a goal ❌ |
|
|
81
|
+
|---------|--------------|
|
|
82
|
+
| Structured architecture | Magic-based auto-wiring |
|
|
83
|
+
| Type safety | Implicit dependency resolution |
|
|
84
|
+
| Clarity and control | Minimal configuration at all costs |
|
|
85
|
+
|
|
86
|
+
👉 This library prioritizes **explicit design over automation**.
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## 🧠 Inspiration
|
|
90
|
+
|
|
91
|
+
Inspired by dependency injection patterns from ecosystems such as:
|
|
92
|
+
|
|
93
|
+
- C# / .NET (built-in DI container)
|
|
94
|
+
- Java (Spring, Guice)
|
|
95
|
+
|
|
96
|
+
But intentionally adapted to fit Python’s dynamic nature
|
|
97
|
+
without introducing heavy frameworks or unnecessary indirection.
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## 🎯 When to use
|
|
101
|
+
|
|
102
|
+
Use this library if you:
|
|
103
|
+
|
|
104
|
+
- Are building medium to large Python applications
|
|
105
|
+
- Want clearer separation between components
|
|
106
|
+
- Need better testability
|
|
107
|
+
- Prefer structured architecture over ad-hoc patterns
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
## 🚫 When not to use
|
|
111
|
+
|
|
112
|
+
This library may be unnecessary if:
|
|
113
|
+
|
|
114
|
+
- Your project is small or short-lived
|
|
115
|
+
- Dependency complexity is minimal
|
|
116
|
+
- Simplicity is more important than structure
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
## 🔗 Related projects
|
|
120
|
+
|
|
121
|
+
Part of a broader focus on:
|
|
122
|
+
|
|
123
|
+
- Clean architecture
|
|
124
|
+
- Runtime abstractions
|
|
125
|
+
- Developer tooling in Python
|
|
126
|
+
|
|
127
|
+
👉 https://github.com/apmadsen
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
## 🤝 Contributing
|
|
132
|
+
|
|
133
|
+
Feedback, ideas, and contributions are welcome!
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
## Example:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from logging import Logger
|
|
140
|
+
from di import Container
|
|
141
|
+
|
|
142
|
+
def get_logger() -> Logger:
|
|
143
|
+
return Logger("app")
|
|
144
|
+
|
|
145
|
+
class Service1:
|
|
146
|
+
def get_value(self) -> str:
|
|
147
|
+
return "Some value"
|
|
148
|
+
|
|
149
|
+
class Service2:
|
|
150
|
+
def __init__(self, service1: Service1, log: Logger):
|
|
151
|
+
self.service1 = service1
|
|
152
|
+
self.log = log
|
|
153
|
+
|
|
154
|
+
def get_value(self) -> str:
|
|
155
|
+
self.log.debug("Someone requested value...")
|
|
156
|
+
return f"Service1 returned: {self.service1.get_value()}"
|
|
157
|
+
|
|
158
|
+
class Application:
|
|
159
|
+
def __init__(self, service2: Service2, log: Logger):
|
|
160
|
+
self.service2 = service2
|
|
161
|
+
log.info("Application starting")
|
|
162
|
+
|
|
163
|
+
def get_value(self) -> str:
|
|
164
|
+
return f"Service2 returned: {self.service2.get_value()}"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
container = Container()
|
|
168
|
+
container.add_singleton(get_logger)
|
|
169
|
+
container.add_transient(Service1)
|
|
170
|
+
container.add_transient(Service2)
|
|
171
|
+
container.add_transient(Application)
|
|
172
|
+
|
|
173
|
+
provider = container.provider() # container is sealed beyond this point
|
|
174
|
+
|
|
175
|
+
app = provider.provide(Application)
|
|
176
|
+
|
|
177
|
+
value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Full documentation
|
|
181
|
+
|
|
182
|
+
[Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
|
|
@@ -22,6 +22,7 @@ classifiers = [
|
|
|
22
22
|
"Programming Language :: Python :: 3.11",
|
|
23
23
|
"Programming Language :: Python :: 3.12",
|
|
24
24
|
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
25
26
|
"Programming Language :: Python",
|
|
26
27
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
28
|
"Topic :: Software Development :: Libraries",
|
|
@@ -29,9 +30,9 @@ classifiers = [
|
|
|
29
30
|
]
|
|
30
31
|
dependencies = [
|
|
31
32
|
"typing-utilities >= 0.1.0, < 0.2",
|
|
32
|
-
"runtime-reflection-lite >= 0.
|
|
33
|
+
"runtime-reflection-lite >= 0.2.0, < 0.3"
|
|
33
34
|
]
|
|
34
|
-
requires-python = ">=3.10"
|
|
35
|
+
requires-python = ">= 3.10, < 3.15"
|
|
35
36
|
|
|
36
37
|
[project.urls]
|
|
37
38
|
repository = "https://github.com/apmadsen/dependency-injection-pattern"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dependency-injection-pattern
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7
|
|
4
4
|
Summary: Python implementation of the Dependency Injection pattern.
|
|
5
5
|
Author-email: Anders Madsen <anders.madsen@alphavue.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,15 +18,16 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Programming Language :: Python
|
|
22
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
24
|
Classifier: Topic :: Software Development :: Libraries
|
|
24
25
|
Classifier: Typing :: Typed
|
|
25
|
-
Requires-Python:
|
|
26
|
+
Requires-Python: <3.15,>=3.10
|
|
26
27
|
Description-Content-Type: text/markdown
|
|
27
28
|
License-File: LICENSE
|
|
28
29
|
Requires-Dist: typing-utilities<0.2,>=0.1.0
|
|
29
|
-
Requires-Dist: runtime-reflection-lite<0.
|
|
30
|
+
Requires-Dist: runtime-reflection-lite<0.3,>=0.2.0
|
|
30
31
|
Provides-Extra: test
|
|
31
32
|
Requires-Dist: pytest>=8.3; extra == "test"
|
|
32
33
|
Requires-Dist: pytest-cov>=6.1; extra == "test"
|
|
@@ -39,22 +40,133 @@ Dynamic: license-file
|
|
|
39
40
|

|
|
40
41
|
[](https://pepy.tech/projects/dependency-injection-pattern)
|
|
41
42
|
|
|
42
|
-
#
|
|
43
|
-
> A Python implementation of the Dependency Injection pattern.
|
|
43
|
+
# Dependency Injection Pattern (Python)
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
A typed dependency injection framework for Python — focused on clarity, structure, and maintainability.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
## 🚀 Why this exists
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
When a service is requested, the provider looks it up in the factory dictionary, which then gets or creates a fitting instance. Any parameters needed for instantiation is supplied (injected) by the provider.
|
|
49
|
+
As Python applications grow, managing dependencies becomes increasingly difficult:
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
- Hidden dependencies make systems harder to understand
|
|
52
|
+
- Tight coupling reduces flexibility
|
|
53
|
+
- Testing requires complex setup and mocking
|
|
54
|
+
- Structure becomes inconsistent across modules
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
Unlike many other ecosystems, Python lacks a widely adopted, structured approach to dependency injection.
|
|
57
|
+
|
|
58
|
+
👉 This project aims to introduce **clear, typed dependency management** to Python applications.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## ✨ Features
|
|
62
|
+
|
|
63
|
+
- 🧩 Strongly typed dependency injection
|
|
64
|
+
- 🧠 Explicit dependency definitions
|
|
65
|
+
- 🔄 Clear separation of concerns
|
|
66
|
+
- 🧪 Improved testability
|
|
67
|
+
- 🏗 Support for structured application architecture
|
|
68
|
+
- ⚡ Lightweight and Python-native design
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
## 📦 Use cases
|
|
72
|
+
|
|
73
|
+
This library is particularly useful when building:
|
|
74
|
+
|
|
75
|
+
- Backend services
|
|
76
|
+
- Data engineering workflows
|
|
77
|
+
- ETL pipelines
|
|
78
|
+
- Modular applications
|
|
79
|
+
- Systems with complex dependency graphs
|
|
80
|
+
|
|
81
|
+
👉 Especially valuable in projects where maintainability and scalability matter.
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## 🏗 Design philosophy
|
|
85
|
+
|
|
86
|
+
This project is built around a few key principles:
|
|
87
|
+
|
|
88
|
+
### 1. Explicit over implicit
|
|
89
|
+
Dependencies should be visible and well-defined — not hidden in implementation details.
|
|
90
|
+
|
|
91
|
+
### 2. Type-driven structure
|
|
92
|
+
Type hints are used as a foundation for defining and resolving dependencies.
|
|
93
|
+
|
|
94
|
+
### 3. Maintainability over convenience
|
|
95
|
+
The goal is not to reduce lines of code — but to make systems easier to evolve.
|
|
96
|
+
|
|
97
|
+
### 4. Minimal abstraction
|
|
98
|
+
The framework avoids unnecessary complexity and stays close to Python’s core concepts.
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
## 🔗 What this enables
|
|
102
|
+
|
|
103
|
+
By introducing structured dependency injection, you get:
|
|
104
|
+
|
|
105
|
+
- Clear boundaries between components
|
|
106
|
+
- Easier reasoning about system design
|
|
107
|
+
- More predictable runtime behavior
|
|
108
|
+
- Improved ability to refactor safely
|
|
109
|
+
|
|
110
|
+
👉 This is especially powerful in larger systems where complexity grows over time.
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## ⚖️ Trade-offs
|
|
114
|
+
|
|
115
|
+
| Focus ✅ | Not a goal ❌ |
|
|
116
|
+
|---------|--------------|
|
|
117
|
+
| Structured architecture | Magic-based auto-wiring |
|
|
118
|
+
| Type safety | Implicit dependency resolution |
|
|
119
|
+
| Clarity and control | Minimal configuration at all costs |
|
|
120
|
+
|
|
121
|
+
👉 This library prioritizes **explicit design over automation**.
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
## 🧠 Inspiration
|
|
125
|
+
|
|
126
|
+
Inspired by dependency injection patterns from ecosystems such as:
|
|
127
|
+
|
|
128
|
+
- C# / .NET (built-in DI container)
|
|
129
|
+
- Java (Spring, Guice)
|
|
130
|
+
|
|
131
|
+
But intentionally adapted to fit Python’s dynamic nature
|
|
132
|
+
without introducing heavy frameworks or unnecessary indirection.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## 🎯 When to use
|
|
136
|
+
|
|
137
|
+
Use this library if you:
|
|
138
|
+
|
|
139
|
+
- Are building medium to large Python applications
|
|
140
|
+
- Want clearer separation between components
|
|
141
|
+
- Need better testability
|
|
142
|
+
- Prefer structured architecture over ad-hoc patterns
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
## 🚫 When not to use
|
|
146
|
+
|
|
147
|
+
This library may be unnecessary if:
|
|
148
|
+
|
|
149
|
+
- Your project is small or short-lived
|
|
150
|
+
- Dependency complexity is minimal
|
|
151
|
+
- Simplicity is more important than structure
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
## 🔗 Related projects
|
|
155
|
+
|
|
156
|
+
Part of a broader focus on:
|
|
157
|
+
|
|
158
|
+
- Clean architecture
|
|
159
|
+
- Runtime abstractions
|
|
160
|
+
- Developer tooling in Python
|
|
161
|
+
|
|
162
|
+
👉 https://github.com/apmadsen
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
## 🤝 Contributing
|
|
167
|
+
|
|
168
|
+
Feedback, ideas, and contributions are welcome!
|
|
56
169
|
|
|
57
|
-
- Singleton services may not depend on scoped or transient services, as this would negate the "transient" or "scoped" part since a singleton service would keep a reference to it's dependencies indefinitely.
|
|
58
170
|
|
|
59
171
|
## Example:
|
|
60
172
|
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/container.py
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from typing import TypeVar, Callable, Any, cast, overload, TYPE_CHECKING
|
|
3
|
-
from typingutils import is_type, isinstance_typing, get_optional_type
|
|
3
|
+
from typingutils import is_type, isinstance_typing, issubclass_typing, get_optional_type
|
|
4
4
|
from types import FunctionType
|
|
5
5
|
from weakref import ref
|
|
6
6
|
from runtime.reflection.lite import get_signature
|
|
@@ -282,7 +282,13 @@ class Container:
|
|
|
282
282
|
elif isinstance(service, type) and issubclass(service, Provider): # pyright: ignore[reportUnnecessaryIsInstance]
|
|
283
283
|
return True
|
|
284
284
|
elif is_type(service):
|
|
285
|
-
|
|
285
|
+
if service in self.__factories:
|
|
286
|
+
return True
|
|
287
|
+
else:
|
|
288
|
+
# check for factories of derived types
|
|
289
|
+
for provided_service, _ in self.__factories.items():
|
|
290
|
+
if issubclass_typing(provided_service, cast(type[Any], service)):
|
|
291
|
+
return True
|
|
286
292
|
|
|
287
293
|
return False
|
|
288
294
|
|
|
@@ -317,6 +323,15 @@ class Container:
|
|
|
317
323
|
factory = self.__factories[cast(type, svc)]
|
|
318
324
|
break
|
|
319
325
|
|
|
326
|
+
# check for factories of derived types
|
|
327
|
+
for svc in [svc_type, service]: # pyright: ignore[reportUnknownVariableType]
|
|
328
|
+
if is_type(svc):
|
|
329
|
+
for provided_service, provided_factory in self.__factories.items():
|
|
330
|
+
if issubclass_typing(provided_service, cast(type[Any], svc)):
|
|
331
|
+
factory = provided_factory
|
|
332
|
+
break
|
|
333
|
+
|
|
334
|
+
|
|
320
335
|
return factory, default, is_optional
|
|
321
336
|
|
|
322
337
|
def _get(self, service: type[T] | ServiceRequest[T]) -> Factory[T] | T | None:
|
|
@@ -42,7 +42,7 @@ class DefaultScope(Scope):
|
|
|
42
42
|
try:
|
|
43
43
|
if is_context(service):
|
|
44
44
|
LOG.debug(f"Disposing service '{get_service_name(service)}' on thread '{current_thread().name}'")
|
|
45
|
-
getattr(implementation, "__exit__")()
|
|
45
|
+
getattr(implementation, "__exit__")(None, None, None)
|
|
46
46
|
|
|
47
47
|
except Exception as ex: # pragma: no cover
|
|
48
48
|
# swallow exception, as it's caught in the finalizer thread
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_provider.py
RENAMED
|
@@ -134,3 +134,23 @@ def test_generic_service():
|
|
|
134
134
|
provider.provide(GenericClass[str])
|
|
135
135
|
provider.provide(GenericClass[int])
|
|
136
136
|
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_derived_service():
|
|
140
|
+
container = Container()
|
|
141
|
+
container.add_singleton(Service)
|
|
142
|
+
provider = container.provider()
|
|
143
|
+
|
|
144
|
+
# the container only has a factory for the Service class which
|
|
145
|
+
# is derived from the IService class, but subclass matching
|
|
146
|
+
# makes sure that both types can be provided
|
|
147
|
+
|
|
148
|
+
assert provider.provides(IService)
|
|
149
|
+
svc = provider.provide(IService)
|
|
150
|
+
assert isinstance(svc, IService)
|
|
151
|
+
assert isinstance(svc, Service)
|
|
152
|
+
|
|
153
|
+
assert provider.provides(Service)
|
|
154
|
+
svc = provider.provide(Service)
|
|
155
|
+
assert isinstance(svc, IService)
|
|
156
|
+
assert isinstance(svc, Service)
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
[](https://github.com/apmadsen/dependency-injection-pattern/actions/workflows/python-test.yml)
|
|
2
|
-
[](https://github.com/apmadsen/dependency-injection-pattern/actions/workflows/python-test-coverage.yml)
|
|
3
|
-
[](https://github.com/apmadsen/dependency-injection-pattern/releases)
|
|
4
|
-

|
|
5
|
-

|
|
6
|
-
[](https://pepy.tech/projects/dependency-injection-pattern)
|
|
7
|
-
|
|
8
|
-
# dependency-injection-pattern
|
|
9
|
-
> A Python implementation of the Dependency Injection pattern.
|
|
10
|
-
|
|
11
|
-
This project extends python with a real dependency injection or Inversion of Control (IoC) implementation, relying on typing and inspection to achieve a seamless injection of dependencies.
|
|
12
|
-
|
|
13
|
-
Though not normally associated with Python applications, dependency injection can greatly reduce time spent during development and testing. Usually, in a longrunning Python application or service, you would put the container registration inside the `__main_.py` file and similar code in test files, where service implementations can easily be substituted.
|
|
14
|
-
|
|
15
|
-
## How it works
|
|
16
|
-
When a service is requested, the provider looks it up in the factory dictionary, which then gets or creates a fitting instance. Any parameters needed for instantiation is supplied (injected) by the provider.
|
|
17
|
-
|
|
18
|
-
A service implementation may be inferred by a service in which case the service constructor is used, and correspondingly a service may be inferred by the return type of an implementation function.
|
|
19
|
-
|
|
20
|
-
## Conventions
|
|
21
|
-
- All dependencies are resolved when container is sealed, which implements a fail-fast pattern. Only exceptions are optional dependencies or dependencies with a default value.
|
|
22
|
-
|
|
23
|
-
- Singleton services may not depend on scoped or transient services, as this would negate the "transient" or "scoped" part since a singleton service would keep a reference to it's dependencies indefinitely.
|
|
24
|
-
|
|
25
|
-
## Example:
|
|
26
|
-
|
|
27
|
-
```python
|
|
28
|
-
from logging import Logger
|
|
29
|
-
from di import Container
|
|
30
|
-
|
|
31
|
-
def get_logger() -> Logger:
|
|
32
|
-
return Logger("app")
|
|
33
|
-
|
|
34
|
-
class Service1:
|
|
35
|
-
def get_value(self) -> str:
|
|
36
|
-
return "Some value"
|
|
37
|
-
|
|
38
|
-
class Service2:
|
|
39
|
-
def __init__(self, service1: Service1, log: Logger):
|
|
40
|
-
self.service1 = service1
|
|
41
|
-
self.log = log
|
|
42
|
-
|
|
43
|
-
def get_value(self) -> str:
|
|
44
|
-
self.log.debug("Someone requested value...")
|
|
45
|
-
return f"Service1 returned: {self.service1.get_value()}"
|
|
46
|
-
|
|
47
|
-
class Application:
|
|
48
|
-
def __init__(self, service2: Service2, log: Logger):
|
|
49
|
-
self.service2 = service2
|
|
50
|
-
log.info("Application starting")
|
|
51
|
-
|
|
52
|
-
def get_value(self) -> str:
|
|
53
|
-
return f"Service2 returned: {self.service2.get_value()}"
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
container = Container()
|
|
57
|
-
container.add_singleton(get_logger)
|
|
58
|
-
container.add_transient(Service1)
|
|
59
|
-
container.add_transient(Service2)
|
|
60
|
-
container.add_transient(Application)
|
|
61
|
-
|
|
62
|
-
provider = container.provider() # container is sealed beyond this point
|
|
63
|
-
|
|
64
|
-
app = provider.provide(Application)
|
|
65
|
-
|
|
66
|
-
value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Full documentation
|
|
70
|
-
|
|
71
|
-
[Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/factory.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/helpers.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/log.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/provider.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/core/scope.py
RENAMED
|
File without changes
|
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/src/di/exceptions.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_container.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_examples.py
RENAMED
|
File without changes
|
{dependency_injection_pattern-0.0.5 → dependency_injection_pattern-0.0.7}/tests/test_factory.py
RENAMED
|
File without changes
|