mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 21:15:41 +00:00
Allow loading of "builtin" plugins, even if "plugins" are not explicitly loaded
This commit is contained in:
@ -7,7 +7,6 @@ The main code for plugin special sauce is in the plugin registry in `InvenTree/p
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
from django.conf import settings
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from maintenance_mode.core import set_maintenance_mode
|
from maintenance_mode.core import set_maintenance_mode
|
||||||
@ -26,34 +25,34 @@ class PluginAppConfig(AppConfig):
|
|||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
"""The ready method is extended to initialize plugins."""
|
"""The ready method is extended to initialize plugins."""
|
||||||
if settings.PLUGINS_ENABLED:
|
if not canAppAccessDatabase(allow_test=True, allow_plugins=True):
|
||||||
if not canAppAccessDatabase(allow_test=True, allow_plugins=True):
|
logger.info("Skipping plugin loading sequence") # pragma: no cover
|
||||||
logger.info("Skipping plugin loading sequence") # pragma: no cover
|
else:
|
||||||
else:
|
logger.info('Loading InvenTree plugins')
|
||||||
logger.info('Loading InvenTree plugins')
|
|
||||||
|
|
||||||
if not registry.is_loading:
|
if not registry.is_loading:
|
||||||
# this is the first startup
|
# this is the first startup
|
||||||
try:
|
try:
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP', create=False, cache=False):
|
if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP', create=False, cache=False):
|
||||||
# make sure all plugins are installed
|
# make sure all plugins are installed
|
||||||
registry.install_plugin_file()
|
registry.install_plugin_file()
|
||||||
except Exception: # pragma: no cover
|
except Exception: # pragma: no cover
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# get plugins and init them
|
# get plugins and init them
|
||||||
registry.plugin_modules = registry.collect_plugins()
|
registry.plugin_modules = registry.collect_plugins()
|
||||||
registry.load_plugins()
|
registry.load_plugins()
|
||||||
|
|
||||||
# drop out of maintenance
|
# drop out of maintenance
|
||||||
# makes sure we did not have an error in reloading and maintenance is still active
|
# makes sure we did not have an error in reloading and maintenance is still active
|
||||||
set_maintenance_mode(False)
|
set_maintenance_mode(False)
|
||||||
|
|
||||||
# check git version
|
# check git version
|
||||||
registry.git_is_modern = check_git_version()
|
registry.git_is_modern = check_git_version()
|
||||||
if not registry.git_is_modern: # pragma: no cover # simulating old git seems not worth it for coverage
|
|
||||||
log_error(_('Your enviroment has an outdated git version. This prevents InvenTree from loading plugin details.'), 'load')
|
if not registry.git_is_modern: # pragma: no cover # simulating old git seems not worth it for coverage
|
||||||
|
log_error(_('Your environment has an outdated git version. This prevents InvenTree from loading plugin details.'), 'load')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.info("Plugins not enabled - skipping loading sequence") # pragma: no cover
|
logger.info("Plugins not enabled - skipping loading sequence") # pragma: no cover
|
||||||
|
@ -106,6 +106,11 @@ class MetaBase:
|
|||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
"""Return True if this plugin is currently active."""
|
"""Return True if this plugin is currently active."""
|
||||||
|
|
||||||
|
# Builtin plugins are always considered "active"
|
||||||
|
if self.is_builtin():
|
||||||
|
return True
|
||||||
|
|
||||||
cfg = self.plugin_config()
|
cfg = self.plugin_config()
|
||||||
|
|
||||||
if cfg:
|
if cfg:
|
||||||
@ -300,6 +305,16 @@ class InvenTreePlugin(VersionMixin, MixinBase, MetaBase):
|
|||||||
"""Is this plugin part of the samples?"""
|
"""Is this plugin part of the samples?"""
|
||||||
return self.check_is_sample()
|
return self.check_is_sample()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_is_builtin(cls) -> bool:
|
||||||
|
"""Determine if a particular plugin class is a 'builtin' plugin"""
|
||||||
|
return str(cls.check_package_path()).startswith('plugin/builtin')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_builtin(self) -> bool:
|
||||||
|
"""Is this plugin is builtin"""
|
||||||
|
return self.check_is_builtin()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_package_path(cls):
|
def check_package_path(cls):
|
||||||
"""Path to the plugin."""
|
"""Path to the plugin."""
|
||||||
|
@ -108,9 +108,6 @@ class PluginsRegistry:
|
|||||||
Args:
|
Args:
|
||||||
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
|
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
|
||||||
"""
|
"""
|
||||||
if not settings.PLUGINS_ENABLED:
|
|
||||||
# Plugins not enabled, do nothing
|
|
||||||
return # pragma: no cover
|
|
||||||
|
|
||||||
logger.info('Start loading plugins')
|
logger.info('Start loading plugins')
|
||||||
|
|
||||||
@ -167,9 +164,6 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
def unload_plugins(self):
|
def unload_plugins(self):
|
||||||
"""Unload and deactivate all IntegrationPlugins."""
|
"""Unload and deactivate all IntegrationPlugins."""
|
||||||
if not settings.PLUGINS_ENABLED:
|
|
||||||
# Plugins not enabled, do nothing
|
|
||||||
return # pragma: no cover
|
|
||||||
|
|
||||||
logger.info('Start unloading plugins')
|
logger.info('Start unloading plugins')
|
||||||
|
|
||||||
@ -187,6 +181,7 @@ class PluginsRegistry:
|
|||||||
# remove maintenance
|
# remove maintenance
|
||||||
if not _maintenance:
|
if not _maintenance:
|
||||||
set_maintenance_mode(False) # pragma: no cover
|
set_maintenance_mode(False) # pragma: no cover
|
||||||
|
|
||||||
logger.info('Finished unloading plugins')
|
logger.info('Finished unloading plugins')
|
||||||
|
|
||||||
def reload_plugins(self, full_reload: bool = False):
|
def reload_plugins(self, full_reload: bool = False):
|
||||||
@ -210,62 +205,63 @@ class PluginsRegistry:
|
|||||||
def plugin_dirs(self):
|
def plugin_dirs(self):
|
||||||
"""Construct a list of directories from where plugins can be loaded"""
|
"""Construct a list of directories from where plugins can be loaded"""
|
||||||
|
|
||||||
|
# Builtin plugins are *always* loaded
|
||||||
dirs = ['plugin.builtin', ]
|
dirs = ['plugin.builtin', ]
|
||||||
|
|
||||||
if settings.TESTING or settings.DEBUG:
|
if settings.PLUGINS_ENABLED:
|
||||||
# If in TEST or DEBUG mode, load plugins from the 'samples' directory
|
# Any 'external' plugins are only loaded if PLUGINS_ENABLED is set to True
|
||||||
dirs.append('plugin.samples')
|
|
||||||
|
|
||||||
if settings.TESTING:
|
if settings.TESTING or settings.DEBUG:
|
||||||
custom_dirs = os.getenv('INVENTREE_PLUGIN_TEST_DIR', None)
|
# If in TEST or DEBUG mode, load plugins from the 'samples' directory
|
||||||
else: # pragma: no cover
|
dirs.append('plugin.samples')
|
||||||
custom_dirs = get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
|
|
||||||
|
|
||||||
# Load from user specified directories (unless in testing mode)
|
if settings.TESTING:
|
||||||
dirs.append('plugins')
|
custom_dirs = os.getenv('INVENTREE_PLUGIN_TEST_DIR', None)
|
||||||
|
else: # pragma: no cover
|
||||||
|
custom_dirs = get_setting('INVENTREE_PLUGIN_DIR', 'plugin_dir')
|
||||||
|
|
||||||
if custom_dirs is not None:
|
# Load from user specified directories (unless in testing mode)
|
||||||
# Allow multiple plugin directories to be specified
|
dirs.append('plugins')
|
||||||
for pd_text in custom_dirs.split(','):
|
|
||||||
pd = Path(pd_text.strip()).absolute()
|
|
||||||
|
|
||||||
# Attempt to create the directory if it does not already exist
|
if custom_dirs is not None:
|
||||||
if not pd.exists():
|
# Allow multiple plugin directories to be specified
|
||||||
try:
|
for pd_text in custom_dirs.split(','):
|
||||||
pd.mkdir(exist_ok=True)
|
pd = Path(pd_text.strip()).absolute()
|
||||||
except Exception: # pragma: no cover
|
|
||||||
logger.error(f"Could not create plugin directory '{pd}'")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Ensure the directory has an __init__.py file
|
# Attempt to create the directory if it does not already exist
|
||||||
init_filename = pd.joinpath('__init__.py')
|
if not pd.exists():
|
||||||
|
try:
|
||||||
|
pd.mkdir(exist_ok=True)
|
||||||
|
except Exception: # pragma: no cover
|
||||||
|
logger.error(f"Could not create plugin directory '{pd}'")
|
||||||
|
continue
|
||||||
|
|
||||||
if not init_filename.exists():
|
# Ensure the directory has an __init__.py file
|
||||||
try:
|
init_filename = pd.joinpath('__init__.py')
|
||||||
init_filename.write_text("# InvenTree plugin directory\n")
|
|
||||||
except Exception: # pragma: no cover
|
|
||||||
logger.error(f"Could not create file '{init_filename}'")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# By this point, we have confirmed that the directory at least exists
|
if not init_filename.exists():
|
||||||
if pd.exists() and pd.is_dir():
|
try:
|
||||||
# Convert to python dot-path
|
init_filename.write_text("# InvenTree plugin directory\n")
|
||||||
if pd.is_relative_to(settings.BASE_DIR):
|
except Exception: # pragma: no cover
|
||||||
pd_path = '.'.join(pd.relative_to(settings.BASE_DIR).parts)
|
logger.error(f"Could not create file '{init_filename}'")
|
||||||
else:
|
continue
|
||||||
pd_path = str(pd)
|
|
||||||
|
|
||||||
# Add path
|
# By this point, we have confirmed that the directory at least exists
|
||||||
dirs.append(pd_path)
|
if pd.exists() and pd.is_dir():
|
||||||
logger.info(f"Added plugin directory: '{pd}' as '{pd_path}'")
|
# Convert to python dot-path
|
||||||
|
if pd.is_relative_to(settings.BASE_DIR):
|
||||||
|
pd_path = '.'.join(pd.relative_to(settings.BASE_DIR).parts)
|
||||||
|
else:
|
||||||
|
pd_path = str(pd)
|
||||||
|
|
||||||
|
# Add path
|
||||||
|
dirs.append(pd_path)
|
||||||
|
logger.info(f"Added plugin directory: '{pd}' as '{pd_path}'")
|
||||||
|
|
||||||
return dirs
|
return dirs
|
||||||
|
|
||||||
def collect_plugins(self):
|
def collect_plugins(self):
|
||||||
"""Collect plugins from all possible ways of loading. Returned as list."""
|
"""Collect plugins from all possible ways of loading. Returned as list."""
|
||||||
if not settings.PLUGINS_ENABLED:
|
|
||||||
# Plugins not enabled, do nothing
|
|
||||||
return # pragma: no cover
|
|
||||||
|
|
||||||
collected_plugins = []
|
collected_plugins = []
|
||||||
|
|
||||||
@ -293,17 +289,20 @@ class PluginsRegistry:
|
|||||||
if modules:
|
if modules:
|
||||||
[collected_plugins.append(item) for item in modules]
|
[collected_plugins.append(item) for item in modules]
|
||||||
|
|
||||||
# Check if not running in testing mode and apps should be loaded from hooks
|
# From this point any plugins are considered "external" and only loaded if plugins are explicitly enabled
|
||||||
if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP):
|
if settings.PLUGINS_ENABLED:
|
||||||
# Collect plugins from setup entry points
|
|
||||||
for entry in get_entrypoints():
|
# Check if not running in testing mode and apps should be loaded from hooks
|
||||||
try:
|
if (not settings.PLUGIN_TESTING) or (settings.PLUGIN_TESTING and settings.PLUGIN_TESTING_SETUP):
|
||||||
plugin = entry.load()
|
# Collect plugins from setup entry points
|
||||||
plugin.is_package = True
|
for entry in get_entrypoints():
|
||||||
plugin._get_package_metadata()
|
try:
|
||||||
collected_plugins.append(plugin)
|
plugin = entry.load()
|
||||||
except Exception as error: # pragma: no cover
|
plugin.is_package = True
|
||||||
handle_error(error, do_raise=False, log_name='discovery')
|
plugin._get_package_metadata()
|
||||||
|
collected_plugins.append(plugin)
|
||||||
|
except Exception as error: # pragma: no cover
|
||||||
|
handle_error(error, do_raise=False, log_name='discovery')
|
||||||
|
|
||||||
# Log collected plugins
|
# Log collected plugins
|
||||||
logger.info(f'Collected {len(collected_plugins)} plugins!')
|
logger.info(f'Collected {len(collected_plugins)} plugins!')
|
||||||
|
Reference in New Issue
Block a user