Make assignments on BaseAddon class objects work as expected

The descriptors were being silently clobbered for a while now, and
I never noticed. Oops!
This commit is contained in:
Salad Dais
2022-07-28 02:41:25 +00:00
parent e951a5b5c3
commit 0cbbedd27b
2 changed files with 43 additions and 2 deletions

View File

@@ -143,7 +143,34 @@ def ais_folder_to_inventory_data(ais_folder: dict):
)
class BaseAddon(abc.ABC):
class MetaBaseAddon(abc.ABCMeta):
"""
Metaclass for BaseAddon that prevents class member assignments from clobbering descriptors
Without this things like:
class Foo(BaseAddon):
bar: int = GlobalProperty(0)
Foo.bar = 2
Won't work as you expect!
"""
def __setattr__(self, key: str, value):
# TODO: Keep track of AddonProperties in __new__ or something?
try:
existing = object.__getattribute__(self, key)
except AttributeError:
# If the attribute doesn't exist then it's fine to use the base setattr.
super().__setattr__(key, value)
return
if existing and isinstance(existing, BaseAddonProperty):
existing.__set__(self, value)
return
super().__setattr__(key, value)
class BaseAddon(metaclass=MetaBaseAddon):
def _schedule_task(self, coro: Coroutine, session=None,
region_scoped=False, session_scoped=True, addon_scoped=True):
session = session or addon_ctx.session.get(None) or None

View File

@@ -33,10 +33,11 @@ class MockAddon(BaseAddon):
PARENT_ADDON_SOURCE = """
from hippolyzer.lib.proxy.addon_utils import BaseAddon
from hippolyzer.lib.proxy.addon_utils import BaseAddon, GlobalProperty
class ParentAddon(BaseAddon):
baz = None
quux: int = GlobalProperty(0)
@classmethod
def foo(cls):
@@ -136,3 +137,16 @@ class AddonIntegrationTests(BaseProxyTest):
AddonManager.unload_addon_from_path(str(self.parent_path), reload=True)
await asyncio.sleep(0.001)
self.assertNotIn('hippolyzer.user_addon_parent_addon', sys.modules)
async def test_global_property_access_and_set(self):
with open(self.parent_path, "w") as f:
f.write(PARENT_ADDON_SOURCE)
AddonManager.load_addon_from_path(str(self.parent_path), reload=True)
# Wait for the init hooks to run
await asyncio.sleep(0.001)
self.assertFalse("quux" in self.session_manager.addon_ctx)
parent_addon_mod = AddonManager.FRESH_ADDON_MODULES['hippolyzer.user_addon_parent_addon']
self.assertEqual(0, parent_addon_mod.ParentAddon.quux)
self.assertEqual(0, self.session_manager.addon_ctx["quux"])
parent_addon_mod.ParentAddon.quux = 1
self.assertEqual(1, self.session_manager.addon_ctx["quux"])