Skip to content

Commit 47fc638

Browse files
committed
Add mechanism to override providers
1 parent 61c13fa commit 47fc638

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

README.md

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ A simple Python dependency injection framework.
1212

1313
**This project is under active development. The following example does not represent the final state for the project.**
1414

15+
### Dependency Injection
16+
1517
The injection framework is configured to inject any default values for method arguments that are instances
1618
of `providers.Provider`.
1719

@@ -21,7 +23,7 @@ arguments at runtime.
2123
All dependency injection is lazily evaluated so providers are only evaluated when a method is called. This approach is
2224
optimal as it reduces necessary computation for expensive services and reduces
2325

24-
### Decorator Injection
26+
#### Decorator Injection
2527

2628
With this approach you can automatically inject functions at load time using the `@wiring.injected` decorator.
2729

@@ -30,7 +32,7 @@ from pif import wiring, providers
3032

3133

3234
@wiring.injected # <- automatically injects providers.Provider default arguments!
33-
def my_function(a: str = providers.Singleton[str](lambda: "hello world")):
35+
def my_function(a: str = providers.Factory[str](lambda: "hello world")):
3436
return a
3537

3638

@@ -46,7 +48,7 @@ With this approach you can wire all methods in the specified modules.
4648
from pif import wiring, providers
4749

4850

49-
def my_function(a: str = providers.Singleton[str](lambda: "hello world")):
51+
def my_function(a: str = providers.Factory[str](lambda: "hello world")):
5052
return a
5153

5254

@@ -56,6 +58,65 @@ if __name__ == "__main__":
5658
assert "hello world" == my_function()
5759
```
5860

61+
### Overriding
62+
63+
This package provides a simple mechanism to override providers. This can be very useful when it comes to mocking
64+
services for testing or dynamically patching application behavior based on application configuration.
65+
66+
#### Standard Overriding
67+
68+
If you want to patch a value all you need to do is call `.override()` on the provider in question.
69+
70+
```python
71+
from pif import wiring, providers
72+
73+
StringProvider = providers.Factory[str](lambda: "hello world")
74+
75+
76+
@wiring.injected
77+
def my_function(a: str = StringProvider):
78+
return a
79+
80+
81+
if __name__ == "__main__":
82+
assert "hello world" == my_function()
83+
84+
override = StringProvider.override(providers.Factory[str](lambda: "overridden_1"))
85+
86+
assert "overridden_1"
87+
```
88+
89+
### Context Managers
90+
91+
If you want more control around the override lifecycles then you may use the `Override` context manager.
92+
93+
```python
94+
from pif import wiring, providers
95+
96+
StringProvider = providers.Factory[str](lambda: "hello world")
97+
98+
99+
@wiring.injected
100+
def my_function(a: str = StringProvider):
101+
return a
102+
103+
104+
if __name__ == "__main__":
105+
assert "hello world" == my_function()
106+
107+
OverrideProvider = providers.Factory[str](lambda: "overridden_1")
108+
109+
with StringProvider.override(OverrideProvider):
110+
assert "overridden_1" == my_function()
111+
112+
with OverrideProvider.override(providers.Factory[str]("overridden_2")):
113+
assert "overridden_2" == my_function() # You can even stack overrides!!
114+
115+
assert "overridden_1" == my_function()
116+
117+
assert "hello world" == my_function()
118+
```
119+
59120
## Authors
60121

61122
| [![Zac Scott](https://avatars.githubusercontent.com/u/38968222?s=128&v=4)](https://github.com/scottzach1) |

pif/providers.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,15 @@ def __init__(self, base: Provider, override: ProviderT | None = None):
6262
def __enter__(self) -> Self:
6363
yield self
6464

65-
def __exit__(self, exc_type, exc_val, exc_tb):
65+
def disable(self) -> None:
66+
"""
67+
Disable the currently active override.
68+
"""
6669
self._base._override = self._before
6770

71+
def __exit__(self, exc_type, exc_val, exc_tb):
72+
self.disable()
73+
6874

6975
class ExistingSingleton[T](Provider):
7076
"""

0 commit comments

Comments
 (0)