Skip to content

Commit

Permalink
Unify clear/once in configure
Browse files Browse the repository at this point in the history
  • Loading branch information
ivankorobkov committed Oct 18, 2023
1 parent 895bc22 commit 7a7fd92
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 15 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,22 +118,23 @@ bar('world')

## Usage with Django
Django can load some modules multiple times which can lead to
`InjectorException: Injector is already configured`. You can use `configure_once` which
`InjectorException: Injector is already configured`. You can use `configure(once=True)` which
is guaranteed to run only once when the injector is absent:
```python
import inject
inject.configure_once(my_config)
inject.configure(my_config, once=True)
```

## Testing
In tests use `inject.clear_and_configure(callable)` to create a new injector on setup,
In tests use `inject.configure(callable, clear=True)` to create a new injector on setup,
and optionally `inject.clear()` to clean up on tear down:
```python
class MyTest(unittest.TestCase):
def setUp(self):
inject.clear_and_configure(lambda binder: binder
inject.configure(lambda binder: binder
.bind(Cache, MockCache()) \
.bind(Validator, TestValidator()))
.bind(Validator, TestValidator()),
clear=True)

def tearDown(self):
inject.clear()
Expand All @@ -155,7 +156,7 @@ You can reuse configurations and override already registered dependencies to fit
binder.bind(Validator, TestValidator())
binder.bind(Cache, MockCache())

inject.clear_and_configure(tests_config, allow_override=True)
inject.configure(tests_config, allow_override=True, clear=True)

```

Expand Down Expand Up @@ -220,8 +221,7 @@ to bind an instance to a class, `inject` will try to implicitly instantiate it.

If an instance is unintentionally created with default arguments it may lead to
hard-to-debug bugs. To disable runtime binding and make sure that only
explicitly bound instances are injected, pass `bind_in_runtime=False`
to `inject.configure`, `inject.configure_once` or `inject.clear_and_configure`.
explicitly bound instances are injected, pass `bind_in_runtime=False` to `inject.configure`.

In this case `inject` immediately raises `InjectorException` when the code
tries to get an unbound instance.
Expand Down
42 changes: 35 additions & 7 deletions src/inject/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,24 +362,41 @@ def injection_wrapper(*args: Any, **kwargs: Any) -> T:


def configure(
config: Optional[BinderCallable] = None, bind_in_runtime: bool = True, allow_override: bool = False
config: Optional[BinderCallable] = None,
bind_in_runtime: bool = True,
allow_override: bool = False,
clear: bool = False,
once: bool = False
) -> Injector:
"""Create an injector with a callable config or raise an exception when already configured."""
global _INJECTOR

if clear and once:
raise InjectorException('clear and once are mutually exclusive, only one can be True')

with _INJECTOR_LOCK:
if _INJECTOR:
raise InjectorException('Injector is already configured')
if clear:
_clear_injector()
elif once:
return _INJECTOR
else:
raise InjectorException('Injector is already configured')

_INJECTOR = Injector(config, bind_in_runtime=bind_in_runtime, allow_override=allow_override)
logger.debug('Created and configured an injector, config=%s', config)
return _INJECTOR


def configure_once(
config: Optional[BinderCallable] = None, bind_in_runtime: bool = True, allow_override: bool = False
config: Optional[BinderCallable] = None,
bind_in_runtime: bool = True,
allow_override: bool = False
) -> Injector:
"""Create an injector with a callable config if not present, otherwise, do nothing."""
"""Create an injector with a callable config if not present, otherwise, do nothing.
Deprecated, use `configure(once=True)` instead.
"""
with _INJECTOR_LOCK:
if _INJECTOR:
return _INJECTOR
Expand All @@ -388,11 +405,16 @@ def configure_once(


def clear_and_configure(
config: Optional[BinderCallable] = None, bind_in_runtime: bool = True, allow_override: bool = False
config: Optional[BinderCallable] = None,
bind_in_runtime: bool = True,
allow_override: bool = False
) -> Injector:
"""Clear an existing injector and create another one with a callable config."""
"""Clear an existing injector and create another one with a callable config.
Deprecated, use configure(clear=True) instead.
"""
with _INJECTOR_LOCK:
clear()
_clear_injector()
return configure(config, bind_in_runtime=bind_in_runtime, allow_override=allow_override)


Expand All @@ -403,6 +425,11 @@ def is_configured() -> bool:


def clear() -> None:
"""Clear an existing injector if present."""
_clear_injector()


def _clear_injector() -> None:
"""Clear an existing injector if present."""
global _INJECTOR

Expand All @@ -413,6 +440,7 @@ def clear() -> None:
_INJECTOR = None
logger.debug('Cleared an injector')


@overload
def instance(cls: Type[T]) -> T: ...

Expand Down

0 comments on commit 7a7fd92

Please sign in to comment.