From 583658cde5851199da6aaa908b0297b57ddfce14 Mon Sep 17 00:00:00 2001 From: Brian Pugh Date: Mon, 23 Oct 2023 17:12:16 -0700 Subject: [PATCH 1/3] Add __setitem__ interface --- autoregistry/_registry.py | 5 +++++ docs/source/Configuration.rst | 1 + tests/test_functions.py | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/autoregistry/_registry.py b/autoregistry/_registry.py index 0b1cc69..a7130d5 100644 --- a/autoregistry/_registry.py +++ b/autoregistry/_registry.py @@ -145,6 +145,9 @@ class _DictMixin: def __getitem__(self, key: str) -> Type: return self.__registry__.config.getitem(self.__registry__, key) + def __setitem__(self, key: str, value: Any): + self.__registry__.register(value, key, root=True) + def __iter__(self) -> Generator[str, None, None]: yield from self.__registry__ @@ -261,6 +264,7 @@ def __new__( if namespace["__registry__"].config.redirect: for method_name in [ "__getitem__", + "__setitem__", "__iter__", "__len__", "__contains__", @@ -306,6 +310,7 @@ class Registry(metaclass=RegistryMeta): __call__: Callable __contains__: Callable[..., bool] __getitem__: Callable[[str], Type] + __setitem__: Callable[[str, Any], Type] __iter__: Callable __len__: Callable[..., int] clear: Callable[[], None] diff --git a/docs/source/Configuration.rst b/docs/source/Configuration.rst index db97e41..c4210c5 100644 --- a/docs/source/Configuration.rst +++ b/docs/source/Configuration.rst @@ -76,6 +76,7 @@ doesn't impact the auto-derived registration key. ``name`` and ``aliases`` values are **not** subject to configured naming rules and will **not** be modified by configurations like ``strip_suffix``. +Similarly, directly setting a registry element ``my_registry["myfunction"] = myfunction`` is not subject to naming rules. However, values are still subject to the ``overwrite`` configuration and will raise ``KeyCollisionError`` if ``name`` or ``aliases`` attempts to overwrite an existing entry while ``overwrite=False``. Additionally, ``name`` and ``aliases`` may **not** contain a ``.`` or a ``/`` due to :ref:`Key Splitting`. diff --git a/tests/test_functions.py b/tests/test_functions.py index f645fb3..d3d609f 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -191,6 +191,27 @@ def bar(): assert list(registry) == ["bar"] +def test_registry_dictionary_assign(): + registry = Registry() + + def bar(): + pass + + registry["foo"] = bar + assert registry["foo"] == bar + + +def test_registry_dictionary_assign_collision(): + registry = Registry() + + def bar(): + pass + + registry["foo"] = bar + with pytest.raises(autoregistry.KeyCollisionError): + registry["foo"] = bar + + def test_registry_module_alias(): import fake_module From 4e820f1580b78d752716b6e6f82470c7d5a8d0f0 Mon Sep 17 00:00:00 2001 From: Brian Pugh Date: Mon, 23 Oct 2023 17:17:07 -0700 Subject: [PATCH 2/3] Add dictionary setitem example to Overview docs --- docs/source/Overview.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/source/Overview.rst b/docs/source/Overview.rst index e4b3aaa..e070c4a 100644 --- a/docs/source/Overview.rst +++ b/docs/source/Overview.rst @@ -98,6 +98,14 @@ object and use it to decorate functions. return 2 * x + # Assigning as you would a dictionary also works + def baz(x): + return 3 * x + + + my_registry["baz"] = baz # The key could be any string. + + # You can also register classes this way. @my_registry class Baz: From 41f5a73ddf9283d27cc5a4eeed5fc7a1d5dd9ef7 Mon Sep 17 00:00:00 2001 From: Brian Pugh Date: Mon, 23 Oct 2023 17:28:15 -0700 Subject: [PATCH 3/3] Don't allow setitem on Registry subclasses --- autoregistry/_registry.py | 6 +++++- tests/test_classes.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/autoregistry/_registry.py b/autoregistry/_registry.py index a7130d5..25d6cf3 100644 --- a/autoregistry/_registry.py +++ b/autoregistry/_registry.py @@ -13,6 +13,7 @@ InvalidNameError, KeyCollisionError, ModuleAliasError, + RegistryError, ) @@ -146,7 +147,10 @@ def __getitem__(self, key: str) -> Type: return self.__registry__.config.getitem(self.__registry__, key) def __setitem__(self, key: str, value: Any): - self.__registry__.register(value, key, root=True) + if type(self) is RegistryDecorator: + self.__registry__.register(value, key, root=True) + else: + raise RegistryError("Cannot directly setitem on a Registry subclass.") def __iter__(self) -> Generator[str, None, None]: yield from self.__registry__ diff --git a/tests/test_classes.py b/tests/test_classes.py index 1bdd55b..b090bb7 100644 --- a/tests/test_classes.py +++ b/tests/test_classes.py @@ -1,6 +1,7 @@ import pytest from common import construct_pokemon_classes +import autoregistry from autoregistry import Registry @@ -81,6 +82,19 @@ def test_defaults_get(): assert Pokemon.get("foo", Charmander) == Charmander +def test_classes_setitem_exception(): + """Don't allow setting items for a Registry subclass since it breaks the hierarchy.""" + + class Foo(Registry): + pass + + def bar(): + pass + + with pytest.raises(autoregistry.RegistryError): + Foo["bar"] = bar + + def test_multiple_inheritence_last(): class Foo: pass