add live reloading

This commit is contained in:
Matthias 2021-11-14 22:01:00 +01:00
parent e3d334f467
commit c059583b08
No known key found for this signature in database
GPG Key ID: F50EF5741D33E076
3 changed files with 109 additions and 11 deletions

View File

@ -864,7 +864,9 @@ PLUGINS = []
INTEGRATION_PLUGINS = {} INTEGRATION_PLUGINS = {}
INTEGRATION_PLUGINS_INACTIVE = {} INTEGRATION_PLUGINS_INACTIVE = {}
INTEGRATION_PLUGIN_GLOBALSETTING = {} INTEGRATION_PLUGIN_GLOBALSETTING = {}
INTEGRATION_APPS_LOADED = False # Marks if apps were reloaded yet INTEGRATION_APPS_LOADED = False # Marks if apps were reloaded yet
INTEGRATION_APPS_PATHS = [] # Holds all added plugin_paths
# Test settings # Test settings
PLUGIN_TESTING = get_setting('PLUGIN_TESTING', TESTING) # used to force enable everything plugin PLUGIN_TESTING = get_setting('PLUGIN_TESTING', TESTING) # used to force enable everything plugin

View File

@ -8,6 +8,8 @@ from typing import OrderedDict
from django.apps import AppConfig, apps from django.apps import AppConfig, apps
from django.conf import settings from django.conf import settings
from django.db.utils import OperationalError, ProgrammingError from django.db.utils import OperationalError, ProgrammingError
from django.conf.urls import url
from django.contrib import admin
try: try:
from importlib import metadata from importlib import metadata
@ -25,14 +27,36 @@ class PluginAppConfig(AppConfig):
def ready(self): def ready(self):
self.collect_plugins() self.collect_plugins()
self.load_plugins()
def load_plugins(self):
logger.info('Start loading plugins')
try: try:
# we are using the db from here - so for migrations etc we need to try this block # we are using the db so for migrations etc we need to try this block
self.init_plugins() self.init_plugins()
self.activate_integration() self.activate_plugins()
except (OperationalError, ProgrammingError): except (OperationalError, ProgrammingError):
# Exception if the database has not been migrated yet # Exception if the database has not been migrated yet
pass logger.info('Database not accessible while loading plugins')
logger.info('Finished loading plugins')
def unload_plugins(self):
logger.info('Start unloading plugins')
# remove all plugins from registry
# plugins = settings.INTEGRATION_PLUGINS
settings.INTEGRATION_PLUGINS = {}
# plugins_inactive = settings.INTEGRATION_PLUGINS_INACTIVE
settings.INTEGRATION_PLUGINS_INACTIVE = {}
# deactivate all integrations
self.deactivate_plugins()
logger.info('Finished unloading plugins')
def reload_plugins(self):
logger.info('Start reloading plugins')
self.unload_plugins()
self.load_plugins()
logger.info('Finished reloading plugins')
def collect_plugins(self): def collect_plugins(self):
"""collect integration plugins from all possible ways of loading""" """collect integration plugins from all possible ways of loading"""
@ -42,6 +66,7 @@ class PluginAppConfig(AppConfig):
if modules: if modules:
[settings.PLUGINS.append(item) for item in modules] [settings.PLUGINS.append(item) for item in modules]
# check if running in testing mode and apps should be loaded from hooks
if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP): if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP):
# Collect plugins from setup entry points # Collect plugins from setup entry points
for entry in metadata.entry_points().get('inventree_plugins', []): for entry in metadata.entry_points().get('inventree_plugins', []):
@ -93,7 +118,7 @@ class PluginAppConfig(AppConfig):
# save for later reference # save for later reference
settings.INTEGRATION_PLUGINS_INACTIVE[plug_key] = plugin_db_setting settings.INTEGRATION_PLUGINS_INACTIVE[plug_key] = plugin_db_setting
def activate_integration(self): def activate_plugins(self):
"""fullfill integrations for all activated plugins""" """fullfill integrations for all activated plugins"""
# activate integrations # activate integrations
plugins = settings.INTEGRATION_PLUGINS.items() plugins = settings.INTEGRATION_PLUGINS.items()
@ -105,6 +130,11 @@ class PluginAppConfig(AppConfig):
# if plugin apps are enabled # if plugin apps are enabled
self.activate_integration_app(plugins) self.activate_integration_app(plugins)
def deactivate_plugins(self):
self.deactivate_integration_app()
self.deactivate_integration_globalsettings()
# region integration_globalsettings
def activate_integration_globalsettings(self, plugins): def activate_integration_globalsettings(self, plugins):
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
@ -118,6 +148,20 @@ class PluginAppConfig(AppConfig):
# Add to settings dir # Add to settings dir
InvenTreeSetting.GLOBAL_SETTINGS.update(plugin_setting) InvenTreeSetting.GLOBAL_SETTINGS.update(plugin_setting)
def deactivate_integration_globalsettings(self):
from common.models import InvenTreeSetting
# collect all settings
plugin_settings = {}
for _, plugin_setting in settings.INTEGRATION_PLUGIN_GLOBALSETTING.items():
plugin_settings.update(plugin_setting)
# remove settings
for setting in plugin_settings:
InvenTreeSetting.GLOBAL_SETTINGS.pop(setting)
# endregion
# region integration_app
def activate_integration_app(self, plugins): def activate_integration_app(self, plugins):
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
@ -137,12 +181,43 @@ class PluginAppConfig(AppConfig):
plugin_path = plugin.PLUGIN_NAME plugin_path = plugin.PLUGIN_NAME
if plugin_path not in settings.INSTALLED_APPS: if plugin_path not in settings.INSTALLED_APPS:
settings.INSTALLED_APPS += [plugin_path] settings.INSTALLED_APPS += [plugin_path]
settings.INTEGRATION_APPS_PATHS += [plugin_path]
apps_changed = True apps_changed = True
# if apps were changed reload
# TODO this is a bit jankey to be honest
if apps_changed: if apps_changed:
# if apps were changed reload
self._reload_apps()
# update urls
self._update_urls()
def deactivate_integration_app(self):
# remove plugin from installed_apps
for plugin in settings.INTEGRATION_APPS_PATHS:
settings.INSTALLED_APPS.remove(plugin)
# reset load flag and reload apps
settings.INTEGRATION_APPS_PATHS = []
settings.INTEGRATION_APPS_LOADED = False
self._reload_apps()
# update urls
self._update_urls()
def _update_urls(self):
from InvenTree.urls import urlpatterns as root_urlpatterns
# add admin urls
new_conf = url(r'^admin/', admin.site.urls, name='inventree-admin')
for index, a in enumerate(root_urlpatterns):
if hasattr(a, 'app_name') and a.app_name == 'admin':
root_urlpatterns[index] = new_conf
print('exchanged')
break
print('done')
def _reload_apps(self):
# TODO this is a bit jankey to be honest
apps.app_configs = OrderedDict() apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False apps.apps_ready = apps.models_ready = apps.loading = apps.ready = False
apps.clear_cache() apps.clear_cache()
apps.populate(settings.INSTALLED_APPS) apps.populate(settings.INSTALLED_APPS)
# endregion

View File

@ -7,6 +7,7 @@ from __future__ import unicode_literals
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.db import models from django.db import models
from django.apps import apps
class PluginConfig(models.Model): class PluginConfig(models.Model):
@ -46,3 +47,23 @@ class PluginConfig(models.Model):
if not self.active: if not self.active:
name += '(not active)' name += '(not active)'
return name return name
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__org_active = self.active
def save(self, force_insert=False, force_update=False, *args, **kwargs):
"""extend save method to reload plugins if the 'active' status changes"""
ret = super().save(force_insert, force_update, *args, **kwargs)
app = apps.get_app_config('plugin')
if self.active is False and self.__org_active is True:
print('deactivated')
app.reload_plugins()
elif self.active is True and self.__org_active is False:
print('activated')
app.reload_plugins()
return ret