From 661cda8135504f8a044c557de45dc0603264e3f6 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 2 Feb 2025 14:58:11 +0100 Subject: [PATCH] Removed dynamic container and its documentation. Added safeguards. --- docs/index.md | 1 - docs/introduction/dynamic-container.md | 25 ------------------------- tests/test_dynamic_container.py | 20 ++++++++------------ that_depends/meta.py | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 38 deletions(-) delete mode 100644 docs/introduction/dynamic-container.md diff --git a/docs/index.md b/docs/index.md index 2c7ea9e4..52a29bff 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,7 +13,6 @@ introduction/inject-factories introduction/scopes introduction/multiple-containers - introduction/dynamic-container introduction/application-settings .. toctree:: diff --git a/docs/introduction/dynamic-container.md b/docs/introduction/dynamic-container.md deleted file mode 100644 index 80b0dd35..00000000 --- a/docs/introduction/dynamic-container.md +++ /dev/null @@ -1,25 +0,0 @@ -# Dynamic container - -You can dynamically assign providers to container: -```python -import datetime - -from tests import container -from that_depends import BaseContainer, providers - - -class DIContainer(BaseContainer): - sync_resource: providers.Resource[datetime.datetime] - async_resource: providers.Resource[datetime.datetime] - - -DIContainer.sync_resource = providers.Resource(container.create_sync_resource) -DIContainer.async_resource = providers.Resource(container.create_async_resource) -``` - -And than you can use these providers as usual: - -```python -sync_resource = await DIContainer.sync_resource() -async_resource = await DIContainer.async_resource() -``` diff --git a/tests/test_dynamic_container.py b/tests/test_dynamic_container.py index 0e2e6215..893a06ba 100644 --- a/tests/test_dynamic_container.py +++ b/tests/test_dynamic_container.py @@ -1,23 +1,19 @@ import datetime +import pytest + from tests import container from that_depends import BaseContainer, providers class DIContainer(BaseContainer): sync_resource: providers.Resource[datetime.datetime] - async_resource: providers.Resource[datetime.datetime] - - -DIContainer.sync_resource = providers.Resource(container.create_sync_resource) -DIContainer.async_resource = providers.Resource(container.create_async_resource) - -async def test_dynamic_container() -> None: - sync_resource = await DIContainer.sync_resource() - async_resource = await DIContainer.async_resource() - assert isinstance(sync_resource, datetime.datetime) - assert isinstance(async_resource, datetime.datetime) +async def test_dynamic_container_not_supported() -> None: + new_provider = providers.Resource(container.create_sync_resource) + with pytest.raises(AttributeError): + DIContainer.sync_resource = new_provider - await DIContainer.tear_down() + with pytest.raises(AttributeError): + DIContainer.something_new = new_provider diff --git a/that_depends/meta.py b/that_depends/meta.py index edb73207..818163d3 100644 --- a/that_depends/meta.py +++ b/that_depends/meta.py @@ -37,6 +37,14 @@ class BaseContainerMeta(abc.ABCMeta): _instances: typing.ClassVar[list[type["BaseContainer"]]] = [] + _MUTABLE_ATTRS = ( + "__abstractmethods__", + "__parameters__", + "_abc_impl", + "providers", + "containers", + ) + _lock: Lock = Lock() @classmethod @@ -56,3 +64,11 @@ def __new__(cls, name: str, bases: tuple[type, ...], namespace: dict[str, typing def get_instances(cls) -> list[type["BaseContainer"]]: """Get all instances that inherit from BaseContainer.""" return cls._instances + + @override + def __setattr__(cls, key: str, value: typing.Any) -> None: + if key in cls._MUTABLE_ATTRS: # Allow modification of mutable attributes + super().__setattr__(key, value) + else: + msg = f"Cannot add new attribute '{key}' to class '{cls.__name__}'" + raise AttributeError(msg)