python-injection 0.6.9__tar.gz → 0.7.0__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.
Potentially problematic release.
This version of python-injection might be problematic. Click here for more details.
- {python_injection-0.6.9 → python_injection-0.7.0}/PKG-INFO +15 -1
- {python_injection-0.6.9 → python_injection-0.7.0}/documentation/basic-usage.md +14 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/_pkg.pyi +27 -4
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/core/module.py +59 -36
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/integrations/blacksheep.py +3 -7
- python_injection-0.7.0/injection/integrations/typer.py +15 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/utils.py +5 -2
- {python_injection-0.6.9 → python_injection-0.7.0}/pyproject.toml +5 -4
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/__init__.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/_pkg.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/common/__init__.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/common/event.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/common/lazy.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/common/tools/__init__.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/common/tools/_type.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/core/__init__.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/exceptions.py +0 -0
- {python_injection-0.6.9 → python_injection-0.7.0}/injection/integrations/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-injection
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Fast and easy dependency injection framework.
|
|
5
5
|
Home-page: https://github.com/100nm/python-injection
|
|
6
6
|
License: MIT
|
|
@@ -137,6 +137,20 @@ class C(B):
|
|
|
137
137
|
...
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
+
If a class is registered in a package and you want to override it, there is the `override` parameter:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
@singleton
|
|
144
|
+
class A:
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
# ...
|
|
148
|
+
|
|
149
|
+
@singleton(on=A, override=True)
|
|
150
|
+
class B(A):
|
|
151
|
+
...
|
|
152
|
+
```
|
|
153
|
+
|
|
140
154
|
## Recipes
|
|
141
155
|
|
|
142
156
|
A recipe is a function that tells the injector how to construct the instance to be injected. It is important to specify
|
|
@@ -120,6 +120,20 @@ class C(B):
|
|
|
120
120
|
...
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
If a class is registered in a package and you want to override it, there is the `override` parameter:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
@singleton
|
|
127
|
+
class A:
|
|
128
|
+
...
|
|
129
|
+
|
|
130
|
+
# ...
|
|
131
|
+
|
|
132
|
+
@singleton(on=A, override=True)
|
|
133
|
+
class B(A):
|
|
134
|
+
...
|
|
135
|
+
```
|
|
136
|
+
|
|
123
137
|
## Recipes
|
|
124
138
|
|
|
125
139
|
A recipe is a function that tells the injector how to construct the instance to be injected. It is important to specify
|
|
@@ -38,12 +38,21 @@ class Module:
|
|
|
38
38
|
|
|
39
39
|
def __init__(self, name: str = ...): ...
|
|
40
40
|
def __contains__(self, cls: type | UnionType, /) -> bool: ...
|
|
41
|
-
def inject(
|
|
41
|
+
def inject(
|
|
42
|
+
self,
|
|
43
|
+
wrapped: Callable[..., Any] = ...,
|
|
44
|
+
/,
|
|
45
|
+
*,
|
|
46
|
+
force: bool = ...,
|
|
47
|
+
):
|
|
42
48
|
"""
|
|
43
49
|
Decorator applicable to a class or function. Inject function dependencies using
|
|
44
50
|
parameter type annotations. If applied to a class, the dependencies resolved
|
|
45
51
|
will be those of the `__init__` method.
|
|
52
|
+
|
|
53
|
+
With `force=True`, parameters passed to replace dependencies will be ignored.
|
|
46
54
|
"""
|
|
55
|
+
|
|
47
56
|
def injectable(
|
|
48
57
|
self,
|
|
49
58
|
wrapped: Callable[..., Any] = ...,
|
|
@@ -51,39 +60,48 @@ class Module:
|
|
|
51
60
|
*,
|
|
52
61
|
cls: type[Injectable] = ...,
|
|
53
62
|
on: type | Iterable[type] | UnionType = ...,
|
|
63
|
+
override: bool = ...,
|
|
54
64
|
):
|
|
55
65
|
"""
|
|
56
66
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
57
67
|
injectable will be constructed. At injection time, a new instance will be
|
|
58
68
|
injected each time.
|
|
59
69
|
"""
|
|
70
|
+
|
|
60
71
|
def singleton(
|
|
61
72
|
self,
|
|
62
73
|
wrapped: Callable[..., Any] = ...,
|
|
63
74
|
/,
|
|
64
75
|
*,
|
|
65
76
|
on: type | Iterable[type] | UnionType = ...,
|
|
77
|
+
override: bool = ...,
|
|
66
78
|
):
|
|
67
79
|
"""
|
|
68
80
|
Decorator applicable to a class or function. It is used to indicate how the
|
|
69
81
|
singleton will be constructed. At injection time, the injected instance will
|
|
70
82
|
always be the same.
|
|
71
83
|
"""
|
|
84
|
+
|
|
72
85
|
def set_constant(
|
|
73
86
|
self,
|
|
74
87
|
instance: _T,
|
|
75
88
|
on: type | Iterable[type] | UnionType = ...,
|
|
89
|
+
*,
|
|
90
|
+
override: bool = ...,
|
|
76
91
|
) -> _T:
|
|
77
92
|
"""
|
|
78
93
|
Function for registering a specific instance to be injected. This is useful for
|
|
79
94
|
registering global variables. The difference with the singleton decorator is
|
|
80
95
|
that no dependencies are resolved, so the module doesn't need to be locked.
|
|
81
96
|
"""
|
|
82
|
-
|
|
97
|
+
|
|
98
|
+
def get_instance(self, cls: type[_T], none: bool = ...) -> _T | None:
|
|
83
99
|
"""
|
|
84
100
|
Function used to retrieve an instance associated with the type passed in
|
|
85
|
-
parameter or return `None
|
|
101
|
+
parameter or return `None` but if `none` parameter is `False` an exception
|
|
102
|
+
will be raised.
|
|
86
103
|
"""
|
|
104
|
+
|
|
87
105
|
def get_lazy_instance(self, cls: type[_T]) -> Lazy[_T | None]:
|
|
88
106
|
"""
|
|
89
107
|
Function used to retrieve an instance associated with the type passed in
|
|
@@ -92,16 +110,19 @@ class Module:
|
|
|
92
110
|
|
|
93
111
|
Example: instance = ~lazy_instance
|
|
94
112
|
"""
|
|
113
|
+
|
|
95
114
|
def use(self, module: Module, priority: ModulePriorities = ...):
|
|
96
115
|
"""
|
|
97
116
|
Function for using another module. Using another module replaces the module's
|
|
98
117
|
dependencies with those of the module used. If the dependency is not found, it
|
|
99
118
|
will be searched for in the module's dependency container.
|
|
100
119
|
"""
|
|
120
|
+
|
|
101
121
|
def stop_using(self, module: Module):
|
|
102
122
|
"""
|
|
103
123
|
Function to remove a module in use.
|
|
104
124
|
"""
|
|
125
|
+
|
|
105
126
|
def use_temporarily(
|
|
106
127
|
self,
|
|
107
128
|
module: Module,
|
|
@@ -110,6 +131,7 @@ class Module:
|
|
|
110
131
|
"""
|
|
111
132
|
Context manager or decorator for temporary use of a module.
|
|
112
133
|
"""
|
|
134
|
+
|
|
113
135
|
def change_priority(self, module: Module, priority: ModulePriorities):
|
|
114
136
|
"""
|
|
115
137
|
Function for changing the priority of a module in use.
|
|
@@ -118,6 +140,7 @@ class Module:
|
|
|
118
140
|
* **LOW**: The module concerned becomes the least important of the modules used.
|
|
119
141
|
* **HIGH**: The module concerned becomes the most important of the modules used.
|
|
120
142
|
"""
|
|
143
|
+
|
|
121
144
|
def unlock(self):
|
|
122
145
|
"""
|
|
123
146
|
Function to unlock the module by deleting cached instances of singletons.
|
|
@@ -130,7 +153,7 @@ class ModulePriorities(Enum):
|
|
|
130
153
|
|
|
131
154
|
@runtime_checkable
|
|
132
155
|
class Injectable(Protocol[_T]):
|
|
133
|
-
def __init__(self, factory: Callable[[], _T] = ...,
|
|
156
|
+
def __init__(self, factory: Callable[[], _T] = ..., /): ...
|
|
134
157
|
@property
|
|
135
158
|
def is_locked(self) -> bool: ...
|
|
136
159
|
def unlock(self): ...
|
|
@@ -61,6 +61,7 @@ class ContainerEvent(Event, ABC):
|
|
|
61
61
|
@dataclass(frozen=True, slots=True)
|
|
62
62
|
class ContainerDependenciesUpdated(ContainerEvent):
|
|
63
63
|
classes: Collection[type]
|
|
64
|
+
override: bool
|
|
64
65
|
|
|
65
66
|
def __str__(self) -> str:
|
|
66
67
|
length = len(self.classes)
|
|
@@ -132,15 +133,13 @@ Injectables
|
|
|
132
133
|
class Injectable(Protocol[_T]):
|
|
133
134
|
__slots__ = ()
|
|
134
135
|
|
|
135
|
-
def __init__(self,
|
|
136
|
-
...
|
|
136
|
+
def __init__(self, __factory: Callable[[], _T] = ..., /): ...
|
|
137
137
|
|
|
138
138
|
@property
|
|
139
139
|
def is_locked(self) -> bool:
|
|
140
140
|
return False
|
|
141
141
|
|
|
142
|
-
def unlock(self):
|
|
143
|
-
...
|
|
142
|
+
def unlock(self): ...
|
|
144
143
|
|
|
145
144
|
@abstractmethod
|
|
146
145
|
def get_instance(self) -> _T:
|
|
@@ -214,26 +213,19 @@ class Container:
|
|
|
214
213
|
def __injectables(self) -> frozenset[Injectable]:
|
|
215
214
|
return frozenset(self.__data.values())
|
|
216
215
|
|
|
217
|
-
def update(self, classes: Types, injectable: Injectable):
|
|
218
|
-
|
|
216
|
+
def update(self, classes: Types, injectable: Injectable, override: bool):
|
|
217
|
+
values = {origin: injectable for origin in get_origins(*classes)}
|
|
219
218
|
|
|
220
|
-
if
|
|
221
|
-
event = ContainerDependenciesUpdated(self,
|
|
219
|
+
if values:
|
|
220
|
+
event = ContainerDependenciesUpdated(self, values, override)
|
|
222
221
|
|
|
223
222
|
with self.notify(event):
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
return self
|
|
223
|
+
if not override:
|
|
224
|
+
self.__check_if_exists(*values)
|
|
229
225
|
|
|
230
|
-
|
|
231
|
-
if cls in self.__data:
|
|
232
|
-
raise RuntimeError(
|
|
233
|
-
f"An injectable already exists for the class `{format_type(cls)}`."
|
|
234
|
-
)
|
|
226
|
+
self.__data.update(values)
|
|
235
227
|
|
|
236
|
-
return
|
|
228
|
+
return self
|
|
237
229
|
|
|
238
230
|
def unlock(self):
|
|
239
231
|
for injectable in self.__injectables:
|
|
@@ -246,6 +238,13 @@ class Container:
|
|
|
246
238
|
def notify(self, event: Event) -> ContextManager | ContextDecorator:
|
|
247
239
|
return self.__channel.dispatch(event)
|
|
248
240
|
|
|
241
|
+
def __check_if_exists(self, *classes: type):
|
|
242
|
+
for cls in classes:
|
|
243
|
+
if cls in self.__data:
|
|
244
|
+
raise RuntimeError(
|
|
245
|
+
f"An injectable already exists for the class `{format_type(cls)}`."
|
|
246
|
+
)
|
|
247
|
+
|
|
249
248
|
|
|
250
249
|
"""
|
|
251
250
|
Module
|
|
@@ -282,7 +281,7 @@ class Module(EventListener):
|
|
|
282
281
|
raise NoInjectable(cls)
|
|
283
282
|
|
|
284
283
|
def __setitem__(self, cls: type | UnionType, injectable: Injectable, /):
|
|
285
|
-
self.update((cls,), injectable)
|
|
284
|
+
self.update((cls,), injectable, override=True)
|
|
286
285
|
|
|
287
286
|
def __contains__(self, cls: type | UnionType, /) -> bool:
|
|
288
287
|
return any(cls in broker for broker in self.__brokers)
|
|
@@ -306,22 +305,29 @@ class Module(EventListener):
|
|
|
306
305
|
*,
|
|
307
306
|
cls: type[Injectable] = NewInjectable,
|
|
308
307
|
on: type | Types = None,
|
|
308
|
+
override: bool = False,
|
|
309
309
|
):
|
|
310
310
|
def decorator(wp):
|
|
311
311
|
factory = self.inject(wp, return_factory=True)
|
|
312
312
|
injectable = cls(factory)
|
|
313
313
|
classes = find_types(wp, on)
|
|
314
|
-
self.update(classes, injectable)
|
|
314
|
+
self.update(classes, injectable, override)
|
|
315
315
|
return wp
|
|
316
316
|
|
|
317
317
|
return decorator(wrapped) if wrapped else decorator
|
|
318
318
|
|
|
319
319
|
singleton = partialmethod(injectable, cls=SingletonInjectable)
|
|
320
320
|
|
|
321
|
-
def set_constant(
|
|
321
|
+
def set_constant(
|
|
322
|
+
self,
|
|
323
|
+
instance: _T,
|
|
324
|
+
on: type | Types = None,
|
|
325
|
+
*,
|
|
326
|
+
override: bool = False,
|
|
327
|
+
) -> _T:
|
|
322
328
|
cls = type(instance)
|
|
323
329
|
|
|
324
|
-
@self.injectable(on=(cls, on))
|
|
330
|
+
@self.injectable(on=(cls, on), override=override)
|
|
325
331
|
def get_constant():
|
|
326
332
|
return instance
|
|
327
333
|
|
|
@@ -332,29 +338,33 @@ class Module(EventListener):
|
|
|
332
338
|
wrapped: Callable[..., Any] = None,
|
|
333
339
|
/,
|
|
334
340
|
*,
|
|
341
|
+
force: bool = False,
|
|
335
342
|
return_factory: bool = False,
|
|
336
343
|
):
|
|
337
344
|
def decorator(wp):
|
|
338
345
|
if not return_factory and isclass(wp):
|
|
339
|
-
wp.__init__ =
|
|
346
|
+
wp.__init__ = self.inject(wp.__init__, force=force)
|
|
340
347
|
return wp
|
|
341
348
|
|
|
342
349
|
lazy_binder = Lazy[Binder](lambda: self.__new_binder(wp))
|
|
343
350
|
|
|
344
351
|
@wraps(wp)
|
|
345
352
|
def wrapper(*args, **kwargs):
|
|
346
|
-
arguments = (~lazy_binder).bind(
|
|
353
|
+
arguments = (~lazy_binder).bind(args, kwargs, force)
|
|
347
354
|
return wp(*arguments.args, **arguments.kwargs)
|
|
348
355
|
|
|
349
356
|
return wrapper
|
|
350
357
|
|
|
351
358
|
return decorator(wrapped) if wrapped else decorator
|
|
352
359
|
|
|
353
|
-
def get_instance(self, cls: type[_T]) -> _T | None:
|
|
360
|
+
def get_instance(self, cls: type[_T], none: bool = True) -> _T | None:
|
|
354
361
|
try:
|
|
355
362
|
injectable = self[cls]
|
|
356
|
-
except KeyError:
|
|
357
|
-
|
|
363
|
+
except KeyError as exc:
|
|
364
|
+
if none:
|
|
365
|
+
return None
|
|
366
|
+
|
|
367
|
+
raise exc from exc
|
|
358
368
|
|
|
359
369
|
instance = injectable.get_instance()
|
|
360
370
|
return cast(cls, instance)
|
|
@@ -362,8 +372,8 @@ class Module(EventListener):
|
|
|
362
372
|
def get_lazy_instance(self, cls: type[_T]) -> Lazy[_T | None]:
|
|
363
373
|
return Lazy(lambda: self.get_instance(cls))
|
|
364
374
|
|
|
365
|
-
def update(self, classes: Types, injectable: Injectable):
|
|
366
|
-
self.__container.update(classes, injectable)
|
|
375
|
+
def update(self, classes: Types, injectable: Injectable, override: bool = False):
|
|
376
|
+
self.__container.update(classes, injectable, override)
|
|
367
377
|
return self
|
|
368
378
|
|
|
369
379
|
def use(
|
|
@@ -476,8 +486,8 @@ class Dependencies:
|
|
|
476
486
|
yield name, injectable.get_instance()
|
|
477
487
|
|
|
478
488
|
@property
|
|
479
|
-
def arguments(self) ->
|
|
480
|
-
return
|
|
489
|
+
def arguments(self) -> OrderedDict[str, Any]:
|
|
490
|
+
return OrderedDict(self)
|
|
481
491
|
|
|
482
492
|
@classmethod
|
|
483
493
|
def from_mapping(cls, mapping: Mapping[str, Injectable]):
|
|
@@ -519,12 +529,26 @@ class Binder(EventListener):
|
|
|
519
529
|
self.__signature = signature
|
|
520
530
|
self.__dependencies = Dependencies.empty()
|
|
521
531
|
|
|
522
|
-
def bind(
|
|
532
|
+
def bind(
|
|
533
|
+
self,
|
|
534
|
+
args: Iterable[Any] = (),
|
|
535
|
+
kwargs: Mapping[str, Any] = None,
|
|
536
|
+
force: bool = False,
|
|
537
|
+
) -> Arguments:
|
|
538
|
+
if kwargs is None:
|
|
539
|
+
kwargs = {}
|
|
540
|
+
|
|
523
541
|
if not self.__dependencies:
|
|
524
542
|
return Arguments(args, kwargs)
|
|
525
543
|
|
|
526
544
|
bound = self.__signature.bind_partial(*args, **kwargs)
|
|
527
|
-
|
|
545
|
+
dependencies = self.__dependencies.arguments
|
|
546
|
+
|
|
547
|
+
if force:
|
|
548
|
+
bound.arguments |= dependencies
|
|
549
|
+
else:
|
|
550
|
+
bound.arguments = dependencies | bound.arguments
|
|
551
|
+
|
|
528
552
|
return Arguments(bound.args, bound.kwargs)
|
|
529
553
|
|
|
530
554
|
def update(self, module: Module):
|
|
@@ -532,8 +556,7 @@ class Binder(EventListener):
|
|
|
532
556
|
return self
|
|
533
557
|
|
|
534
558
|
@singledispatchmethod
|
|
535
|
-
def on_event(self, event: Event, /):
|
|
536
|
-
...
|
|
559
|
+
def on_event(self, event: Event, /): ...
|
|
537
560
|
|
|
538
561
|
@on_event.register
|
|
539
562
|
@contextmanager
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from typing import Any, TypeVar
|
|
2
2
|
|
|
3
3
|
from injection import Module, default_module
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
__all__ = ("InjectionServices",)
|
|
5
6
|
|
|
6
7
|
_T = TypeVar("_T")
|
|
7
8
|
|
|
@@ -24,9 +25,4 @@ class InjectionServices:
|
|
|
24
25
|
return self
|
|
25
26
|
|
|
26
27
|
def resolve(self, cls: type[_T] | Any, *__args, **__kwargs) -> _T:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if instance is None:
|
|
30
|
-
raise NoInjectable(cls)
|
|
31
|
-
|
|
32
|
-
return instance
|
|
28
|
+
return self.__module.get_instance(cls, none=False)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from typer import Option
|
|
2
|
+
|
|
3
|
+
__all__ = ("ignore",)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def ignore():
|
|
7
|
+
"""
|
|
8
|
+
Typer option for the CLI to ignore this option and replace it with `None`.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
return Option(
|
|
12
|
+
default_factory=str,
|
|
13
|
+
parser=lambda _: None,
|
|
14
|
+
hidden=True,
|
|
15
|
+
)
|
|
@@ -5,11 +5,14 @@ from types import ModuleType
|
|
|
5
5
|
__all__ = ("load_package",)
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def load_package(package: ModuleType):
|
|
8
|
+
def load_package(package: ModuleType | str):
|
|
9
9
|
"""
|
|
10
10
|
Function for importing all modules in a Python package.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
if isinstance(package, str):
|
|
14
|
+
package = import_module(package)
|
|
15
|
+
|
|
13
16
|
try:
|
|
14
17
|
path = package.__path__
|
|
15
18
|
except AttributeError as exc:
|
|
@@ -17,7 +20,7 @@ def load_package(package: ModuleType):
|
|
|
17
20
|
"Package has no `__path__` attribute, as it's probably a module."
|
|
18
21
|
) from exc
|
|
19
22
|
|
|
20
|
-
for info in walk_packages(path, prefix=f"{package.__name__}."):
|
|
23
|
+
for info in walk_packages(path=path, prefix=f"{package.__name__}."):
|
|
21
24
|
if info.ispkg:
|
|
22
25
|
continue
|
|
23
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-injection"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "Fast and easy dependency injection framework."
|
|
5
5
|
authors = ["remimd"]
|
|
6
6
|
keywords = ["dependencies", "inject", "injection"]
|
|
@@ -14,13 +14,14 @@ python = ">=3.10, <4"
|
|
|
14
14
|
|
|
15
15
|
[tool.poetry.group.dev.dependencies]
|
|
16
16
|
black = "*"
|
|
17
|
-
blacksheep = "^2.0.
|
|
17
|
+
blacksheep = "^2.0.6"
|
|
18
18
|
flake8 = "*"
|
|
19
19
|
isort = "*"
|
|
20
|
-
pydantic = "^2.
|
|
21
|
-
pytest = "
|
|
20
|
+
pydantic = "^2.6.1"
|
|
21
|
+
pytest = "<8"
|
|
22
22
|
pytest-asyncio = "*"
|
|
23
23
|
pytest-cov = "*"
|
|
24
|
+
typer = "^0.9.0"
|
|
24
25
|
|
|
25
26
|
[build-system]
|
|
26
27
|
requires = ["poetry-core"]
|
|
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
|