Cleanup plugin mixin registry (#4790)

* collect mixins dynamically

* remove unfinsihed option to reorder mixins

* clean up settings

* fix text

* fix mixin lookup

* stupid error

* fix assertations

* use regustered function instead of private dict

* switch to dict for reg

* fix test

* makke sure mixins also works with class

* cleanup

* fix reqs

* fix test assertations
This commit is contained in:
Matthias Mair 2023-05-12 14:00:25 +02:00 committed by GitHub
parent 17057f4266
commit 017ccaa27a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 20 deletions

View File

@ -24,9 +24,9 @@ class BaseMixinDefinition:
def test_mixin_name(self): def test_mixin_name(self):
"""Test that the mixin registers itseld correctly.""" """Test that the mixin registers itseld correctly."""
# mixin name # mixin name
self.assertIn(self.MIXIN_NAME, [item['key'] for item in self.mixin.registered_mixins]) self.assertIn(self.MIXIN_NAME, {item['key'] for item in self.mixin.registered_mixins.values()})
# human name # human name
self.assertIn(self.MIXIN_HUMAN_NAME, [item['human_name'] for item in self.mixin.registered_mixins]) self.assertIn(self.MIXIN_HUMAN_NAME, {item['human_name'] for item in self.mixin.registered_mixins.values()})
class SettingsMixinTest(BaseMixinDefinition, InvenTreeTestCase): class SettingsMixinTest(BaseMixinDefinition, InvenTreeTestCase):

View File

@ -1,5 +1,6 @@
"""Plugin model definitions.""" """Plugin model definitions."""
import inspect
import warnings import warnings
from django.conf import settings from django.conf import settings
@ -58,7 +59,9 @@ class PluginConfig(models.Model):
def mixins(self): def mixins(self):
"""Returns all registered mixins.""" """Returns all registered mixins."""
try: try:
return self.plugin._mixinreg if inspect.isclass(self.plugin):
return self.plugin.get_registered_mixins(self, with_base=True, with_cls=False)
return self.plugin.get_registered_mixins(with_base=True, with_cls=False)
except (AttributeError, ValueError): # pragma: no cover except (AttributeError, ValueError): # pragma: no cover
return {} return {}
@ -166,7 +169,9 @@ class PluginSetting(common.models.BaseInvenTreeSetting):
if issubclass(plugin.__class__, InvenTreePlugin): if issubclass(plugin.__class__, InvenTreePlugin):
plugin = plugin.plugin_config() plugin = plugin.plugin_config()
kwargs['settings'] = registry.mixins_settings.get(plugin.key, {}) mixin_settings = getattr(registry, 'mixins_settings')
if mixin_settings:
kwargs['settings'] = mixin_settings.get(plugin.key, {})
return super().get_setting_definition(key, **kwargs) return super().get_setting_definition(key, **kwargs)

View File

@ -161,19 +161,29 @@ class MixinBase:
self._mixinreg[key] = { self._mixinreg[key] = {
'key': key, 'key': key,
'human_name': human_name, 'human_name': human_name,
'cls': cls,
} }
def get_registered_mixins(self, with_base: bool = False, with_cls: bool = True):
"""Get all registered mixins for the plugin."""
mixins = getattr(self, '_mixinreg', None)
if not mixins:
return {}
mixins = mixins.copy()
# filter out base
if not with_base and 'base' in mixins:
del mixins['base']
# Do not return the mixin class if flas is set
if not with_cls:
return {key: {k: v for k, v in mixin.items() if k != 'cls'} for key, mixin in mixins.items()}
return mixins
@property @property
def registered_mixins(self, with_base: bool = False): def registered_mixins(self, with_base: bool = False):
"""Get all registered mixins for the plugin.""" """Get all registered mixins for the plugin."""
mixins = getattr(self, '_mixinreg', None) return self.get_registered_mixins(with_base=with_base)
if mixins:
# filter out base
if not with_base and 'base' in mixins:
del mixins['base']
# only return dict
mixins = list(mixins.values())
return mixins
class VersionMixin: class VersionMixin:

View File

@ -42,7 +42,7 @@ class PluginsRegistry:
DEFAULT_MIXIN_ORDER = [SettingsMixin, ScheduleMixin, AppMixin, UrlsMixin] DEFAULT_MIXIN_ORDER = [SettingsMixin, ScheduleMixin, AppMixin, UrlsMixin]
def __init__(self, mixin_order: list = None) -> None: def __init__(self) -> None:
"""Initialize registry. """Initialize registry.
Set up all needed references for internal and external states. Set up all needed references for internal and external states.
@ -53,6 +53,7 @@ class PluginsRegistry:
self.plugins_full: Dict[str, InvenTreePlugin] = {} # List of all plugin instances self.plugins_full: Dict[str, InvenTreePlugin] = {} # List of all plugin instances
self.plugin_modules: List(InvenTreePlugin) = [] # Holds all discovered plugins self.plugin_modules: List(InvenTreePlugin) = [] # Holds all discovered plugins
self.mixin_modules: Dict[str, any] = {} # Holds all discovered mixins
self.errors = {} # Holds discovering errors self.errors = {} # Holds discovering errors
@ -63,10 +64,6 @@ class PluginsRegistry:
self.installed_apps = [] # Holds all added plugin_paths self.installed_apps = [] # Holds all added plugin_paths
# mixins
self.mixins_settings = {}
self.mixin_order = mixin_order or self.DEFAULT_MIXIN_ORDER
def get_plugin(self, slug): def get_plugin(self, slug):
"""Lookup plugin by slug (unique key).""" """Lookup plugin by slug (unique key)."""
if slug not in self.plugins: if slug not in self.plugins:
@ -322,6 +319,15 @@ class PluginsRegistry:
return collected_plugins return collected_plugins
def discover_mixins(self):
"""Discover all mixins from plugins and register them."""
collected_mixins = {}
for plg in self.plugins.values():
collected_mixins.update(plg.get_registered_mixins())
self.mixin_modules = collected_mixins
def install_plugin_file(self): def install_plugin_file(self):
"""Make sure all plugins are installed in the current environment.""" """Make sure all plugins are installed in the current environment."""
if settings.PLUGIN_FILE_CHECKED: if settings.PLUGIN_FILE_CHECKED:
@ -468,6 +474,17 @@ class PluginsRegistry:
else: # pragma: no cover else: # pragma: no cover
safe_reference(plugin=plg, key=plg_key, active=False) safe_reference(plugin=plg, key=plg_key, active=False)
def __get_mixin_order(self):
"""Returns a list of mixin classes, in the order that they should be activated."""
# Preset list of mixins
order = self.DEFAULT_MIXIN_ORDER
# Append mixins that are not defined in the default list
order += [m.get('cls') for m in self.mixin_modules.values() if m.get('cls') not in order]
# Final list of mixins
return order
def _activate_plugins(self, force_reload=False, full_reload: bool = False): def _activate_plugins(self, force_reload=False, full_reload: bool = False):
"""Run activation functions for all plugins. """Run activation functions for all plugins.
@ -475,11 +492,14 @@ class PluginsRegistry:
force_reload (bool, optional): Also reload base apps. Defaults to False. force_reload (bool, optional): Also reload base apps. Defaults to False.
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False. full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
""" """
# activate integrations # Collect mixins
self.discover_mixins()
# Activate integrations
plugins = self.plugins.items() plugins = self.plugins.items()
logger.info(f'Found {len(plugins)} active plugins') logger.info(f'Found {len(plugins)} active plugins')
for mixin in self.mixin_order: for mixin in self.__get_mixin_order():
if hasattr(mixin, '_activate_mixin'): if hasattr(mixin, '_activate_mixin'):
mixin._activate_mixin(self, plugins, force_reload=force_reload, full_reload=full_reload) mixin._activate_mixin(self, plugins, force_reload=force_reload, full_reload=full_reload)
@ -491,7 +511,7 @@ class PluginsRegistry:
Args: Args:
force_reload (bool, optional): Also reload base apps. Defaults to False. force_reload (bool, optional): Also reload base apps. Defaults to False.
""" """
for mixin in self.mixin_order: for mixin in reversed(self.__get_mixin_order()):
if hasattr(mixin, '_deactivate_mixin'): if hasattr(mixin, '_deactivate_mixin'):
mixin._deactivate_mixin(self, force_reload=force_reload) mixin._deactivate_mixin(self, force_reload=force_reload)