From 7e57196d76f4560caebbff9508ea5e230341fc08 Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Thu, 12 Nov 2026 08:41:59 +0100 Subject: [PATCH] Add import linter (#13248) * add import-linter * move machine imports to plugin space * clean up samples * extend scope * extend checks * move plugin internals into a seperat folder * remove unneeded re-export * adjust import * update import checker * update src ref * fix mocks --- .pre-commit-config.yaml | 8 ++ docs/docs/plugins/develop.md | 2 +- pyproject.toml | 105 ++++++++++++++++++ src/backend/InvenTree/InvenTree/backup.py | 4 +- src/backend/InvenTree/InvenTree/helpers.py | 3 +- .../templatetags/inventree_extras.py | 2 +- src/backend/InvenTree/InvenTree/test_api.py | 3 +- src/backend/InvenTree/InvenTree/unit_test.py | 2 +- src/backend/InvenTree/data_exporter/mixins.py | 2 +- src/backend/InvenTree/machine/registry.py | 4 +- src/backend/InvenTree/machine/test_api.py | 2 +- src/backend/InvenTree/machine/tests.py | 2 +- src/backend/InvenTree/plugin/__init__.py | 7 +- .../InvenTree/plugin/_internal/__init__.py | 0 .../plugin/{plugin.py => _internal/base.py} | 5 +- .../plugin/{ => _internal}/helpers.py | 2 +- .../plugin/{ => _internal}/installer.py | 4 +- .../InvenTree/plugin/{ => _internal}/lease.py | 0 .../plugin/{ => _internal}/registry.py | 12 +- src/backend/InvenTree/plugin/admin.py | 2 +- src/backend/InvenTree/plugin/api.py | 3 +- .../InvenTree/plugin/base/event/events.py | 3 +- .../InvenTree/plugin/base/event/mixins.py | 3 +- .../InvenTree/plugin/base/icons/mixins.py | 3 +- .../plugin/base/integration/APICallMixin.py | 3 +- .../base/integration/CurrencyExchangeMixin.py | 3 +- .../plugin/base/integration/MachineMixin.py | 2 +- .../base/integration/NavigationMixin.py | 3 +- .../plugin/base/integration/ScheduleMixin.py | 3 +- .../plugin/base/integration/SettingsMixin.py | 4 +- .../plugin/base/integration/WellKnownMixin.py | 3 +- .../plugin/base/integration/test_mixins.py | 3 +- .../InvenTree/plugin/base/label/label.py | 2 +- .../InvenTree/plugin/base/label/mixins.py | 3 +- .../plugin/base/label/test_label_mixin.py | 5 +- .../InvenTree/plugin/base/locate/api.py | 3 +- .../InvenTree/plugin/base/locate/mixins.py | 3 +- .../InvenTree/plugin/base/mail/mail.py | 3 +- .../InvenTree/plugin/base/mail/mixins.py | 3 +- .../InvenTree/plugin/base/supplier/api.py | 3 +- .../builtin/barcodes/inventree_barcode.py | 14 ++- .../builtin/events/auto_create_builds.py | 3 +- .../plugin/builtin/exporter/test_exporter.py | 2 +- .../builtin/integration/machine_types.py | 5 +- .../integration/test_core_wellknown.py | 2 +- .../builtin/labels/inventree_machine.py | 2 +- .../suppliers/test_supplier_barcodes.py | 2 +- .../InvenTree/plugin/machine/__init__.py | 5 +- .../InvenTree/plugin/machine/machine_types.py | 7 +- .../InvenTree/plugin/mixins/__init__.py | 11 ++ src/backend/InvenTree/plugin/models.py | 3 +- .../plugin/samples/event/test_event_sample.py | 6 +- .../event/test_filtered_event_sample.py | 2 +- .../plugin/samples/icons/icon_sample.py | 4 +- .../plugin/samples/icons/test_icon_sample.py | 3 +- .../integration/test_allocate_sample.py | 2 +- .../integration/test_scheduled_task.py | 9 +- .../integration/test_simpleactionplugin.py | 2 +- .../integration/test_validation_sample.py | 2 +- .../samples/locate/test_locate_sample.py | 3 +- .../plugin/samples/machines/sample_printer.py | 3 +- .../plugin/samples/mail/test_mail_sample.py | 7 +- .../samples/supplier/supplier_sample.py | 2 +- src/backend/InvenTree/plugin/serializers.py | 6 +- src/backend/InvenTree/plugin/staticfiles.py | 4 +- .../plugin/templatetags/plugin_extras.py | 2 +- src/backend/InvenTree/plugin/test_api.py | 11 +- src/backend/InvenTree/plugin/test_plugin.py | 78 +++++++------ .../InvenTree/plugin/test_staticfiles.py | 4 +- .../plugin/testing/label_machines.py | 3 +- src/backend/InvenTree/plugin/urls.py | 6 +- src/backend/InvenTree/report/models.py | 3 +- src/backend/InvenTree/report/tasks.py | 2 +- src/backend/InvenTree/report/tests.py | 2 +- src/backend/InvenTree/stock/tests.py | 4 +- 75 files changed, 284 insertions(+), 174 deletions(-) create mode 100644 src/backend/InvenTree/plugin/_internal/__init__.py rename src/backend/InvenTree/plugin/{plugin.py => _internal/base.py} (99%) rename src/backend/InvenTree/plugin/{ => _internal}/helpers.py (99%) rename src/backend/InvenTree/plugin/{ => _internal}/installer.py (99%) rename src/backend/InvenTree/plugin/{ => _internal}/lease.py (100%) rename src/backend/InvenTree/plugin/{ => _internal}/registry.py (99%) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f8b69234f..ec89ea06a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -64,6 +64,14 @@ repos: rev: v1.36.4 hooks: - id: djlint-django +- repo: local + hooks: + - id: import-linter + name: import-linter + entry: bash -c 'cd src/backend/InvenTree && lint-imports --config ../../../pyproject.toml' + language: python + additional_dependencies: [import-linter==2.16] + pass_filenames: false - repo: https://github.com/codespell-project/codespell rev: v2.4.2 hooks: diff --git a/docs/docs/plugins/develop.md b/docs/docs/plugins/develop.md index cda90d14f3..4011a17ad1 100644 --- a/docs/docs/plugins/develop.md +++ b/docs/docs/plugins/develop.md @@ -52,7 +52,7 @@ from plugin.mixins import APICallMixin, SettingsMixin, ScheduleMixin, BarcodeMix ### Plugin Base Class -Custom plugins must inherit from the [InvenTreePlugin class]({{ sourcefile("src/backend/InvenTree/plugin/plugin.py") }}). Any plugins installed via the methods outlined above will be "discovered" when the InvenTree server launches. +Custom plugins must inherit from the [InvenTreePlugin class]({{ sourcefile("src/backend/InvenTree/plugin/_internal/base.py") }}). Any plugins installed via the methods outlined above will be "discovered" when the InvenTree server launches. ### Imports diff --git a/pyproject.toml b/pyproject.toml index 5beaf5875f..87f8c04734 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -142,3 +142,108 @@ pythonpath = ["src/backend/InvenTree"] DJANGO_SETTINGS_MODULE = "InvenTree.settings" python_files = ["test*.py",] timeout = "120" + +[tool.importlinter] +# Architectural import boundaries for the backend, enforced by import-linter (prek). +root_packages = [ + "InvenTree", + "audit", + "build", + "common", + "company", + "data_exporter", + "generic", + "importer", + "machine", + "order", + "part", + "plugin", + "plugins", + "report", + "stock", + "users", + "web", +] +include_external_packages = false + +[[tool.importlinter.contracts]] +# Plugins are only allowed to use the documented, stable import targets: +# `plugin` , `plugin.mixins`, `plugin.machine` +name = "Plugins only import the public plugin API" +type = "forbidden" +source_modules = ["plugin.builtin", "plugin.samples", "plugins"] +forbidden_modules = [ + "machine", + "plugin", + "plugin._internal", + "plugin.admin", + "plugin.api", + "plugin.apps", + "plugin.base", + "plugin.broken", + "plugin.builtin", + "plugin.events", + # "plugin.machine", - allowed + "plugin.migrations", + # "plugin.mixins", - allowed + "plugin.mock", + "plugin.models", + "plugin.samples", + "plugin.serializers", + "plugin.staticfiles", + "plugin.template", + "plugin.test_api", + "plugin.test_plugin", + "plugin.test_staticfiles", + "plugin.testing", + "plugin.urls", +] +allow_indirect_imports = true + +# [[tool.importlinter.contracts]] +# name = "Plugin mixins must be layered" +# type = "independence" +# modules=[ +# "plugin.base.*" +# ] + + +[[tool.importlinter.contracts]] +# `generic` holds shared enums, events and state machines +name = "Generic app does not depend on feature apps" +type = "forbidden" +source_modules = ["generic"] +forbidden_modules = [ + "build", + "company", + "importer", + "machine", + "order", + "part", + "report", + "stock", + "users", + "web", +] +allow_indirect_imports = true +ignore_imports = [ + "generic.states.tests -> build.status_codes", + "generic.states.test_transition -> order.models", + "generic.states.test_transition -> order.status_codes", + "generic.states.api_helpers -> users.permissions", + "generic.states.test_transition -> users.models", +] + +[[tool.importlinter.contracts]] +# Ban dependency cycles between sibling modules/subpackages +name = "No dependency cycles inside an app" +type = "acyclic_siblings" +ancestors = [ + "company", + "data_exporter", + "generic", + "report", + "stock", + "web", +] +ignore_imports = ["generic.templatetags.generic -> generic.states.tags"] diff --git a/src/backend/InvenTree/InvenTree/backup.py b/src/backend/InvenTree/InvenTree/backup.py index 1bb9b40aeb..99b4d9f55c 100644 --- a/src/backend/InvenTree/InvenTree/backup.py +++ b/src/backend/InvenTree/InvenTree/backup.py @@ -158,7 +158,7 @@ InvenTreeBackupMetadata = dict[str, str | int | bool | None] def _gather_environment_metadata() -> InvenTreeBackupMetadata: """Gather metadata about the current environment to be stored with the backup.""" - import plugin.installer + from plugin._internal.installer import plugins_file_hash new_data: InvenTreeBackupMetadata = {} @@ -166,7 +166,7 @@ def _gather_environment_metadata() -> InvenTreeBackupMetadata: new_data['ivt_1_version'] = InvenTree.version.inventreeVersion() new_data['ivt_1_version_api'] = InvenTree.version.inventreeApiVersion() new_data['ivt_1_plugins_enabled'] = settings.PLUGINS_ENABLED - new_data['ivt_1_plugins_file_hash'] = plugin.installer.plugins_file_hash() + new_data['ivt_1_plugins_file_hash'] = plugins_file_hash() new_data['ivt_1_installer'] = InvenTree.config.inventreeInstaller() new_data['ivt_1_backup_time'] = datetime.now().isoformat() diff --git a/src/backend/InvenTree/InvenTree/helpers.py b/src/backend/InvenTree/InvenTree/helpers.py index d67d37f23b..9daeacccbb 100644 --- a/src/backend/InvenTree/InvenTree/helpers.py +++ b/src/backend/InvenTree/InvenTree/helpers.py @@ -1154,8 +1154,7 @@ def pui_url(subpath: str) -> str: def plugins_info(*args, **kwargs): """Return information about activated plugins.""" - from plugin import PluginMixinEnum - from plugin.registry import registry + from plugin import PluginMixinEnum, registry # Check if plugins are even enabled if not settings.PLUGINS_ENABLED: diff --git a/src/backend/InvenTree/InvenTree/templatetags/inventree_extras.py b/src/backend/InvenTree/InvenTree/templatetags/inventree_extras.py index 0a37a097d8..0b4d2f7cab 100644 --- a/src/backend/InvenTree/InvenTree/templatetags/inventree_extras.py +++ b/src/backend/InvenTree/InvenTree/templatetags/inventree_extras.py @@ -13,7 +13,7 @@ import InvenTree.helpers import plugin.models from common.settings import get_global_setting from InvenTree import version -from plugin.plugin import InvenTreePlugin +from plugin import InvenTreePlugin register = template.Library() diff --git a/src/backend/InvenTree/InvenTree/test_api.py b/src/backend/InvenTree/InvenTree/test_api.py index 1db8834f9d..564280bdc9 100644 --- a/src/backend/InvenTree/InvenTree/test_api.py +++ b/src/backend/InvenTree/InvenTree/test_api.py @@ -868,9 +868,8 @@ class GeneralApiTests(InvenTreeAPITestCase): def test_info_view(self): """Test that we can read the 'info-view' endpoint.""" - from plugin import PluginMixinEnum + from plugin import PluginMixinEnum, registry from plugin.models import PluginConfig - from plugin.registry import registry self.ensurePluginsLoaded() diff --git a/src/backend/InvenTree/InvenTree/unit_test.py b/src/backend/InvenTree/InvenTree/unit_test.py index 6f5044bf7f..f1f5d00a30 100644 --- a/src/backend/InvenTree/InvenTree/unit_test.py +++ b/src/backend/InvenTree/InvenTree/unit_test.py @@ -473,7 +473,7 @@ class PluginRegistryMixin: from time import sleep from common.models import InvenTreeSetting - from plugin.registry import registry + from plugin import registry while not registry.is_ready: print('Waiting for plugin registry to be ready...') diff --git a/src/backend/InvenTree/data_exporter/mixins.py b/src/backend/InvenTree/data_exporter/mixins.py index 14886876c9..003e119494 100644 --- a/src/backend/InvenTree/data_exporter/mixins.py +++ b/src/backend/InvenTree/data_exporter/mixins.py @@ -328,7 +328,7 @@ class DataExportViewMixin: - If a plugin is specified, the plugin can be used to augment or replace the export functionality. """ if export_plugin is None: - from plugin.registry import registry + from plugin import registry export_plugin = registry.get_plugin('inventree-exporter') diff --git a/src/backend/InvenTree/machine/registry.py b/src/backend/InvenTree/machine/registry.py index 3f90cbef23..e1b3a78dec 100644 --- a/src/backend/InvenTree/machine/registry.py +++ b/src/backend/InvenTree/machine/registry.py @@ -137,7 +137,7 @@ class MachineRegistry( def discover_machine_types(self): """Discovers all machine types by discovering all plugins which implement the Machine mixin class.""" from plugin import PluginMixinEnum - from plugin.registry import registry as plugin_registry + from plugin import registry as plugin_registry logger.debug('Collecting machine types') @@ -187,7 +187,7 @@ class MachineRegistry( def discover_drivers(self): """Discovers all machine drivers by discovering all plugins which implement the Machine mixin class.""" from plugin import PluginMixinEnum - from plugin.registry import registry as plugin_registry + from plugin import registry as plugin_registry logger.debug('Collecting machine drivers') drivers: dict[str, type[BaseDriver]] = {} diff --git a/src/backend/InvenTree/machine/test_api.py b/src/backend/InvenTree/machine/test_api.py index 6fc9821203..1e66d3fc77 100644 --- a/src/backend/InvenTree/machine/test_api.py +++ b/src/backend/InvenTree/machine/test_api.py @@ -10,7 +10,7 @@ from machine import registry from machine.machine_type import BaseDriver from machine.models import MachineConfig from machine.tests import TestMachineRegistryMixin -from plugin.registry import registry as plg_registry +from plugin import registry as plg_registry from stock.models import StockLocation diff --git a/src/backend/InvenTree/machine/tests.py b/src/backend/InvenTree/machine/tests.py index 2feeb6c12e..729d782cc5 100755 --- a/src/backend/InvenTree/machine/tests.py +++ b/src/backend/InvenTree/machine/tests.py @@ -10,8 +10,8 @@ from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase from machine.models import MachineConfig from machine.registry import registry from part.models import Part +from plugin import registry as plg_registry from plugin.models import PluginConfig -from plugin.registry import registry as plg_registry from report.models import LabelTemplate diff --git a/src/backend/InvenTree/plugin/__init__.py b/src/backend/InvenTree/plugin/__init__.py index 9d0fb43a8e..1b528fcd8b 100644 --- a/src/backend/InvenTree/plugin/__init__.py +++ b/src/backend/InvenTree/plugin/__init__.py @@ -1,13 +1,14 @@ """Utility file to enable simper imports.""" -from .helpers import MixinImplementationError, MixinNotImplementedError -from .plugin import InvenTreePlugin, PluginMixinEnum -from .registry import registry +from ._internal.base import InvenTreePlugin, PluginMixinEnum +from ._internal.helpers import MixinImplementationError, MixinNotImplementedError +from ._internal.registry import call_plugin_function, registry __all__ = [ 'InvenTreePlugin', 'MixinImplementationError', 'MixinNotImplementedError', 'PluginMixinEnum', + 'call_plugin_function', 'registry', ] diff --git a/src/backend/InvenTree/plugin/_internal/__init__.py b/src/backend/InvenTree/plugin/_internal/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/backend/InvenTree/plugin/plugin.py b/src/backend/InvenTree/plugin/_internal/base.py similarity index 99% rename from src/backend/InvenTree/plugin/plugin.py rename to src/backend/InvenTree/plugin/_internal/base.py index a32d62e2e7..6d8ebd1d7f 100644 --- a/src/backend/InvenTree/plugin/plugin.py +++ b/src/backend/InvenTree/plugin/_internal/base.py @@ -19,7 +19,8 @@ import structlog import InvenTree.helpers from generic.enums import StringEnum -from plugin.helpers import get_git_log + +from .helpers import get_git_log logger = structlog.get_logger('inventree') @@ -173,7 +174,7 @@ class MetaBase: @mark_final def plugin_config(self): """Return the PluginConfig object associated with this plugin.""" - from plugin.registry import registry + from .registry import registry return registry.get_plugin_config(self.plugin_slug()) diff --git a/src/backend/InvenTree/plugin/helpers.py b/src/backend/InvenTree/plugin/_internal/helpers.py similarity index 99% rename from src/backend/InvenTree/plugin/helpers.py rename to src/backend/InvenTree/plugin/_internal/helpers.py index 22cf3e2986..661a74efd1 100644 --- a/src/backend/InvenTree/plugin/helpers.py +++ b/src/backend/InvenTree/plugin/_internal/helpers.py @@ -52,7 +52,7 @@ class MixinNotImplementedError(NotImplementedError): def log_registry_error(error, reference: str = 'general'): """Log an plugin error.""" - from plugin.registry import registry + from .registry import registry # make sure the registry is set up if reference not in registry.errors: diff --git a/src/backend/InvenTree/plugin/installer.py b/src/backend/InvenTree/plugin/_internal/installer.py similarity index 99% rename from src/backend/InvenTree/plugin/installer.py rename to src/backend/InvenTree/plugin/_internal/installer.py index 1647a974b0..6364e91f3c 100644 --- a/src/backend/InvenTree/plugin/installer.py +++ b/src/backend/InvenTree/plugin/_internal/installer.py @@ -368,7 +368,7 @@ def install_plugin( update_plugins_file(package_reference=package_ref) # Reload the plugin registry, to discover the new plugin - from plugin.registry import registry + from .registry import registry registry.reload_plugins(full_reload=True, force_reload=True, collect=True) @@ -404,7 +404,7 @@ def uninstall_plugin(cfg: plugin.models.PluginConfig, user=None, delete_config=T user: User performing the uninstall delete_config: If True, delete the plugin configuration from the database """ - from plugin.registry import registry + from .registry import registry if user and not user.is_superuser: raise ValidationError(_('Only superuser accounts can administer plugins')) diff --git a/src/backend/InvenTree/plugin/lease.py b/src/backend/InvenTree/plugin/_internal/lease.py similarity index 100% rename from src/backend/InvenTree/plugin/lease.py rename to src/backend/InvenTree/plugin/_internal/lease.py diff --git a/src/backend/InvenTree/plugin/registry.py b/src/backend/InvenTree/plugin/_internal/registry.py similarity index 99% rename from src/backend/InvenTree/plugin/registry.py rename to src/backend/InvenTree/plugin/_internal/registry.py index bb321ec697..93a50a57e1 100644 --- a/src/backend/InvenTree/plugin/registry.py +++ b/src/backend/InvenTree/plugin/_internal/registry.py @@ -33,6 +33,7 @@ from common.settings import get_global_setting, set_global_setting from InvenTree.config import get_plugin_dir from InvenTree.exceptions import log_error +from .base import InvenTreePlugin from .helpers import ( IntegrationPluginError, MixinNotImplementedError, @@ -42,7 +43,6 @@ from .helpers import ( log_registry_error, ) from .lease import release_lease, try_acquire_lease -from .plugin import InvenTreePlugin logger = structlog.get_logger('inventree') @@ -81,10 +81,10 @@ def registry_entrypoint(check_reload: bool = True, default_value: Any = None) -> class PluginsRegistry: """The PluginsRegistry class.""" - from .base.integration.AppMixin import AppMixin - from .base.integration.ScheduleMixin import ScheduleMixin - from .base.integration.SettingsMixin import SettingsMixin - from .base.integration.UrlsMixin import UrlsMixin + from ..base.integration.AppMixin import AppMixin + from ..base.integration.ScheduleMixin import ScheduleMixin + from ..base.integration.SettingsMixin import SettingsMixin + from ..base.integration.UrlsMixin import UrlsMixin DEFAULT_MIXIN_ORDER = [SettingsMixin, ScheduleMixin, AppMixin, UrlsMixin] @@ -834,7 +834,7 @@ class PluginsRegistry: - Also store the hash as a local marker file in the current environment """ - from plugin.installer import ( + from .installer import ( get_env_plugin_hash, install_plugins_file, plugins_file_hash, diff --git a/src/backend/InvenTree/plugin/admin.py b/src/backend/InvenTree/plugin/admin.py index ff88df7de3..3691e39e9e 100644 --- a/src/backend/InvenTree/plugin/admin.py +++ b/src/backend/InvenTree/plugin/admin.py @@ -4,7 +4,7 @@ from django.contrib import admin from common.admin_shared import NoAddAdminMixin from plugin import models -from plugin.registry import registry as pl_registry +from plugin import registry as pl_registry def plugin_update(queryset, new_status: bool): diff --git a/src/backend/InvenTree/plugin/api.py b/src/backend/InvenTree/plugin/api.py index 9b1685e158..e3bb93b75f 100644 --- a/src/backend/InvenTree/plugin/api.py +++ b/src/backend/InvenTree/plugin/api.py @@ -28,14 +28,13 @@ from InvenTree.mixins import ( RetrieveUpdateAPI, UpdateAPI, ) +from plugin import InvenTreePlugin, registry from plugin.base.action.api import ActionPluginView from plugin.base.barcodes.api import barcode_api_urls from plugin.base.locate.api import LocatePluginView from plugin.base.supplier.api import supplier_api_urls from plugin.base.ui.api import ui_plugins_api_urls from plugin.models import PluginConfig, PluginSetting, PluginUserSetting -from plugin.plugin import InvenTreePlugin -from plugin.registry import registry class PluginFilter(FilterSet): diff --git a/src/backend/InvenTree/plugin/base/event/events.py b/src/backend/InvenTree/plugin/base/event/events.py index 19f242e55d..319633ac0e 100644 --- a/src/backend/InvenTree/plugin/base/event/events.py +++ b/src/backend/InvenTree/plugin/base/event/events.py @@ -16,8 +16,7 @@ import InvenTree.exceptions from common.settings import get_global_setting from InvenTree.ready import canAppAccessDatabase, isImportingData, isRunningMigrations from InvenTree.tasks import bulk_offload_task, offload_task -from plugin import PluginMixinEnum -from plugin.registry import registry +from plugin import PluginMixinEnum, registry tracer = trace.get_tracer(__name__) logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/event/mixins.py b/src/backend/InvenTree/plugin/base/event/mixins.py index cba2f659c3..d179d70ec9 100644 --- a/src/backend/InvenTree/plugin/base/event/mixins.py +++ b/src/backend/InvenTree/plugin/base/event/mixins.py @@ -1,7 +1,6 @@ """Plugin mixin class for events.""" -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum class EventMixin: diff --git a/src/backend/InvenTree/plugin/base/icons/mixins.py b/src/backend/InvenTree/plugin/base/icons/mixins.py index 801d280296..96561bc7e2 100644 --- a/src/backend/InvenTree/plugin/base/icons/mixins.py +++ b/src/backend/InvenTree/plugin/base/icons/mixins.py @@ -3,8 +3,7 @@ import structlog from common.icons import IconPack, reload_icon_packs -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/integration/APICallMixin.py b/src/backend/InvenTree/plugin/base/integration/APICallMixin.py index 85cd21825f..8300bae0b3 100644 --- a/src/backend/InvenTree/plugin/base/integration/APICallMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/APICallMixin.py @@ -7,8 +7,7 @@ from typing import Optional import requests import structlog -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py b/src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py index 08f7794de4..c5843cfc4e 100644 --- a/src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/CurrencyExchangeMixin.py @@ -1,7 +1,6 @@ """Plugin mixin class for supporting currency exchange data.""" -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum class CurrencyExchangeMixin: diff --git a/src/backend/InvenTree/plugin/base/integration/MachineMixin.py b/src/backend/InvenTree/plugin/base/integration/MachineMixin.py index c7f0efcde4..940eefcd91 100644 --- a/src/backend/InvenTree/plugin/base/integration/MachineMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/MachineMixin.py @@ -2,8 +2,8 @@ import structlog -from machine.machine_type import BaseDriver, BaseMachineType from plugin import PluginMixinEnum +from plugin.machine import BaseDriver, BaseMachineType logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/integration/NavigationMixin.py b/src/backend/InvenTree/plugin/base/integration/NavigationMixin.py index 937f46d298..a7a2bf5240 100644 --- a/src/backend/InvenTree/plugin/base/integration/NavigationMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/NavigationMixin.py @@ -2,8 +2,7 @@ import structlog -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py b/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py index 624784f776..0ad8611c6f 100644 --- a/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/ScheduleMixin.py @@ -6,8 +6,7 @@ from django.db.utils import OperationalError, ProgrammingError import structlog from common.settings import get_global_setting -from plugin import PluginMixinEnum -from plugin.helpers import MixinImplementationError +from plugin import MixinImplementationError, PluginMixinEnum logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/integration/SettingsMixin.py b/src/backend/InvenTree/plugin/base/integration/SettingsMixin.py index 34216b0261..6f0559021f 100644 --- a/src/backend/InvenTree/plugin/base/integration/SettingsMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/SettingsMixin.py @@ -100,8 +100,8 @@ class SettingsMixin: value: The value to be set for the setting user: The user who is making the change (optional) """ + from plugin import registry from plugin.models import PluginSetting - from plugin.registry import registry try: plugin = registry.get_plugin_config(self.plugin_slug(), self.plugin_name()) @@ -145,8 +145,8 @@ class SettingsMixin: value: The value to be set for the user setting user: The user for which the setting is to be set """ + from plugin import registry from plugin.models import PluginUserSetting - from plugin.registry import registry try: plugin = registry.get_plugin_config(self.plugin_slug(), self.plugin_name()) diff --git a/src/backend/InvenTree/plugin/base/integration/WellKnownMixin.py b/src/backend/InvenTree/plugin/base/integration/WellKnownMixin.py index 79688eb505..17d152a56d 100644 --- a/src/backend/InvenTree/plugin/base/integration/WellKnownMixin.py +++ b/src/backend/InvenTree/plugin/base/integration/WellKnownMixin.py @@ -1,7 +1,6 @@ """Plugin mixin class for adding well-known urls.""" -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum class WellKnownMixin: diff --git a/src/backend/InvenTree/plugin/base/integration/test_mixins.py b/src/backend/InvenTree/plugin/base/integration/test_mixins.py index dfe6dff239..246fa628b7 100644 --- a/src/backend/InvenTree/plugin/base/integration/test_mixins.py +++ b/src/backend/InvenTree/plugin/base/integration/test_mixins.py @@ -10,8 +10,7 @@ from django.urls import include, path, re_path import requests_mock from InvenTree.unit_test import InvenTreeTestCase -from plugin import InvenTreePlugin -from plugin.helpers import MixinNotImplementedError +from plugin import InvenTreePlugin, MixinNotImplementedError from plugin.mixins import ( AllocateMixin, APICallMixin, diff --git a/src/backend/InvenTree/plugin/base/label/label.py b/src/backend/InvenTree/plugin/base/label/label.py index d99badd928..bbb8c7e8fb 100644 --- a/src/backend/InvenTree/plugin/base/label/label.py +++ b/src/backend/InvenTree/plugin/base/label/label.py @@ -7,7 +7,7 @@ import structlog import common.notifications from InvenTree.exceptions import log_error -from plugin.registry import registry +from plugin import registry logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/label/mixins.py b/src/backend/InvenTree/plugin/base/label/mixins.py index a979cdcc68..e2c5057ae2 100644 --- a/src/backend/InvenTree/plugin/base/label/mixins.py +++ b/src/backend/InvenTree/plugin/base/label/mixins.py @@ -10,9 +10,8 @@ from rest_framework.request import Request from common.models import DataOutput, InvenTreeSetting from InvenTree.exceptions import log_error from InvenTree.tasks import offload_task -from plugin import PluginMixinEnum +from plugin import MixinNotImplementedError, PluginMixinEnum from plugin.base.label import label as plugin_label -from plugin.helpers import MixinNotImplementedError from report.models import LabelTemplate diff --git a/src/backend/InvenTree/plugin/base/label/test_label_mixin.py b/src/backend/InvenTree/plugin/base/label/test_label_mixin.py index a75e933c14..f84bc97bf2 100644 --- a/src/backend/InvenTree/plugin/base/label/test_label_mixin.py +++ b/src/backend/InvenTree/plugin/base/label/test_label_mixin.py @@ -15,12 +15,11 @@ from rest_framework import serializers from InvenTree.config import get_testfolder_dir from InvenTree.unit_test import InvenTreeAPITestCase -from machine import registry as machine_registry from machine.models import MachineConfig from part.models import Part -from plugin import InvenTreePlugin, PluginMixinEnum, registry +from plugin import InvenTreePlugin, MixinNotImplementedError, PluginMixinEnum, registry from plugin.base.label.mixins import LabelPrintingMixin -from plugin.helpers import MixinNotImplementedError +from plugin.machine import registry as machine_registry from report.models import LabelTemplate from report.tests import PrintTestMixins from stock.models import StockItem, StockLocation diff --git a/src/backend/InvenTree/plugin/base/locate/api.py b/src/backend/InvenTree/plugin/base/locate/api.py index 957d45ca69..b98d4f552a 100644 --- a/src/backend/InvenTree/plugin/base/locate/api.py +++ b/src/backend/InvenTree/plugin/base/locate/api.py @@ -13,8 +13,7 @@ from rest_framework.response import Response import InvenTree.permissions from InvenTree.exceptions import log_error from InvenTree.tasks import offload_task -from plugin import PluginMixinEnum, registry -from plugin.registry import call_plugin_function +from plugin import PluginMixinEnum, call_plugin_function, registry from stock.models import StockItem, StockLocation from users.permissions import check_user_permission diff --git a/src/backend/InvenTree/plugin/base/locate/mixins.py b/src/backend/InvenTree/plugin/base/locate/mixins.py index b1986f3cf5..f7e24be23e 100644 --- a/src/backend/InvenTree/plugin/base/locate/mixins.py +++ b/src/backend/InvenTree/plugin/base/locate/mixins.py @@ -2,8 +2,7 @@ import structlog -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/base/mail/mail.py b/src/backend/InvenTree/plugin/base/mail/mail.py index 51b1f51d86..3fdd510439 100644 --- a/src/backend/InvenTree/plugin/base/mail/mail.py +++ b/src/backend/InvenTree/plugin/base/mail/mail.py @@ -7,8 +7,7 @@ from django.core.mail.message import EmailMessage, EmailMultiAlternatives from common.models import EmailMessage as InventreeEmailMessage from common.settings import get_global_setting from InvenTree.backends import logger -from plugin.plugin import PluginMixinEnum -from plugin.registry import registry +from plugin import PluginMixinEnum, registry def process_mail_out( diff --git a/src/backend/InvenTree/plugin/base/mail/mixins.py b/src/backend/InvenTree/plugin/base/mail/mixins.py index 9c1287b18f..09af40e6b6 100644 --- a/src/backend/InvenTree/plugin/base/mail/mixins.py +++ b/src/backend/InvenTree/plugin/base/mail/mixins.py @@ -2,8 +2,7 @@ from django.core.mail import EmailMessage -from plugin import PluginMixinEnum -from plugin.helpers import MixinNotImplementedError +from plugin import MixinNotImplementedError, PluginMixinEnum class MailMixin: diff --git a/src/backend/InvenTree/plugin/base/supplier/api.py b/src/backend/InvenTree/plugin/base/supplier/api.py index 3c288a238d..0ab2b1aa3a 100644 --- a/src/backend/InvenTree/plugin/base/supplier/api.py +++ b/src/backend/InvenTree/plugin/base/supplier/api.py @@ -13,8 +13,7 @@ from rest_framework.views import APIView from InvenTree import permissions from part.models import PartCategoryParameterTemplate -from plugin import registry -from plugin.plugin import PluginMixinEnum +from plugin import PluginMixinEnum, registry from .serializers import ( ImportRequestSerializer, diff --git a/src/backend/InvenTree/plugin/builtin/barcodes/inventree_barcode.py b/src/backend/InvenTree/plugin/builtin/barcodes/inventree_barcode.py index c2f163fe8f..52e31b894a 100644 --- a/src/backend/InvenTree/plugin/builtin/barcodes/inventree_barcode.py +++ b/src/backend/InvenTree/plugin/builtin/barcodes/inventree_barcode.py @@ -13,11 +13,15 @@ from typing import cast from django.utils.translation import gettext_lazy as _ -import plugin.base.barcodes.helper from InvenTree.helpers import hash_barcode from InvenTree.models import InvenTreeBarcodeMixin from plugin import InvenTreePlugin -from plugin.mixins import BarcodeMixin, SettingsMixin +from plugin.mixins import ( + BarcodeMixin, + SettingsMixin, + get_supported_barcode_model_codes_map, + get_supported_barcode_models, +) class InvenTreeInternalBarcodePlugin(SettingsMixin, BarcodeMixin, InvenTreePlugin): @@ -67,9 +71,7 @@ class InvenTreeInternalBarcodePlugin(SettingsMixin, BarcodeMixin, InvenTreePlugi ): model_type_code, pk = m.groups() - supported_models_map = ( - plugin.base.barcodes.helper.get_supported_barcode_model_codes_map() - ) + supported_models_map = get_supported_barcode_model_codes_map() model = supported_models_map.get(model_type_code, None) if model is None: @@ -96,7 +98,7 @@ class InvenTreeInternalBarcodePlugin(SettingsMixin, BarcodeMixin, InvenTreePlugi except json.JSONDecodeError: pass - supported_models = plugin.base.barcodes.helper.get_supported_barcode_models() + supported_models = get_supported_barcode_models() succcess_message = _('Found matching item') diff --git a/src/backend/InvenTree/plugin/builtin/events/auto_create_builds.py b/src/backend/InvenTree/plugin/builtin/events/auto_create_builds.py index d38295838b..943e0501b3 100644 --- a/src/backend/InvenTree/plugin/builtin/events/auto_create_builds.py +++ b/src/backend/InvenTree/plugin/builtin/events/auto_create_builds.py @@ -16,9 +16,8 @@ from build.events import BuildEvents from build.models import Build from build.status_codes import BuildStatus, BuildStatusGroups from InvenTree.tasks import offload_task -from plugin import InvenTreePlugin +from plugin import InvenTreePlugin, call_plugin_function from plugin.mixins import EventMixin, SettingsMixin -from plugin.registry import call_plugin_function logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/builtin/exporter/test_exporter.py b/src/backend/InvenTree/plugin/builtin/exporter/test_exporter.py index 654c575ef6..bc4c5d3fe5 100644 --- a/src/backend/InvenTree/plugin/builtin/exporter/test_exporter.py +++ b/src/backend/InvenTree/plugin/builtin/exporter/test_exporter.py @@ -5,7 +5,7 @@ from django.urls import reverse from InvenTree.helpers import escape_export_value from InvenTree.unit_test import InvenTreeAPITestCase -from plugin.registry import registry +from plugin import registry class StocktakeExporterTest(InvenTreeAPITestCase): diff --git a/src/backend/InvenTree/plugin/builtin/integration/machine_types.py b/src/backend/InvenTree/plugin/builtin/integration/machine_types.py index 33b7c28664..2eec35e859 100644 --- a/src/backend/InvenTree/plugin/builtin/integration/machine_types.py +++ b/src/backend/InvenTree/plugin/builtin/integration/machine_types.py @@ -2,8 +2,9 @@ from django.utils.translation import gettext_lazy as _ -from machine.machine_type import BaseDriver, BaseMachineType from plugin import InvenTreePlugin +from plugin.machine import BaseDriver, BaseMachineType +from plugin.machine.machine_types import LabelPrinterMachine from plugin.mixins import MachineDriverMixin @@ -19,8 +20,6 @@ class InvenTreeMachineTypes(MachineDriverMixin, InvenTreePlugin): def get_machine_types(self) -> list[BaseMachineType]: """Return all built-in machine types.""" - from machine.machine_types.label_printer import LabelPrinterMachine - return [LabelPrinterMachine] def get_machine_drivers(self) -> list[BaseDriver]: diff --git a/src/backend/InvenTree/plugin/builtin/integration/test_core_wellknown.py b/src/backend/InvenTree/plugin/builtin/integration/test_core_wellknown.py index c55d550517..8873baa29b 100644 --- a/src/backend/InvenTree/plugin/builtin/integration/test_core_wellknown.py +++ b/src/backend/InvenTree/plugin/builtin/integration/test_core_wellknown.py @@ -5,7 +5,7 @@ from django.urls import reverse from common.models import InvenTreeSetting from InvenTree.unit_test import InvenTreeAPITestCase -from plugin.registry import registry +from plugin import registry class InvenTreeWellKnownTest(InvenTreeAPITestCase): diff --git a/src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py b/src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py index e6265c8eb5..616fad7310 100644 --- a/src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py +++ b/src/backend/InvenTree/plugin/builtin/labels/inventree_machine.py @@ -10,9 +10,9 @@ from rest_framework import serializers from common.models import InvenTreeUserSetting from InvenTree.serializers import DependentField from InvenTree.tasks import offload_task -from machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine from plugin import InvenTreePlugin from plugin.machine import call_machine_function, registry +from plugin.machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine from plugin.mixins import LabelPrintingMixin from report.models import LabelTemplate diff --git a/src/backend/InvenTree/plugin/builtin/suppliers/test_supplier_barcodes.py b/src/backend/InvenTree/plugin/builtin/suppliers/test_supplier_barcodes.py index 10909ff6aa..c86f8feaff 100644 --- a/src/backend/InvenTree/plugin/builtin/suppliers/test_supplier_barcodes.py +++ b/src/backend/InvenTree/plugin/builtin/suppliers/test_supplier_barcodes.py @@ -6,7 +6,7 @@ from company.models import Company, ManufacturerPart, SupplierPart from InvenTree.unit_test import InvenTreeAPITestCase from order.models import PurchaseOrder, PurchaseOrderLineItem from part.models import Part -from plugin.registry import registry +from plugin import registry from stock.models import StockItem, StockLocation diff --git a/src/backend/InvenTree/plugin/machine/__init__.py b/src/backend/InvenTree/plugin/machine/__init__.py index 98a601d146..5d534339e1 100644 --- a/src/backend/InvenTree/plugin/machine/__init__.py +++ b/src/backend/InvenTree/plugin/machine/__init__.py @@ -1,11 +1,10 @@ -from machine import ( +from machine.machine_type import ( BaseDriver, BaseMachineType, MachineProperty, MachineStatus, - registry, ) -from machine.registry import call_machine_function +from machine.registry import call_machine_function, registry __all__ = [ 'BaseDriver', diff --git a/src/backend/InvenTree/plugin/machine/machine_types.py b/src/backend/InvenTree/plugin/machine/machine_types.py index bd94ed7537..50fdb37c5b 100644 --- a/src/backend/InvenTree/plugin/machine/machine_types.py +++ b/src/backend/InvenTree/plugin/machine/machine_types.py @@ -1,3 +1,8 @@ """just re-export the machine types from the plugin InvenTree app.""" -from machine.machine_types import * # noqa: F403 +from machine.machine_types.label_printer import ( + LabelPrinterBaseDriver, + LabelPrinterMachine, +) + +__all__ = ['LabelPrinterBaseDriver', 'LabelPrinterMachine'] diff --git a/src/backend/InvenTree/plugin/mixins/__init__.py b/src/backend/InvenTree/plugin/mixins/__init__.py index 5a799baa6f..abfbaf9b0a 100644 --- a/src/backend/InvenTree/plugin/mixins/__init__.py +++ b/src/backend/InvenTree/plugin/mixins/__init__.py @@ -1,6 +1,10 @@ """Utility class to enable simpler imports.""" from plugin.base.action.mixins import ActionMixin +from plugin.base.barcodes.helper import ( + get_supported_barcode_model_codes_map, + get_supported_barcode_models, +) from plugin.base.barcodes.mixins import BarcodeMixin, SupplierBarcodeMixin from plugin.base.event.mixins import EventMixin from plugin.base.icons.mixins import IconPackMixin @@ -21,10 +25,12 @@ from plugin.base.integration.ValidationMixin import ValidationMixin from plugin.base.integration.WellKnownMixin import WellKnownMixin from plugin.base.label.mixins import LabelPrintingMixin from plugin.base.locate.mixins import LocateMixin +from plugin.base.mail.mail import process_mail_in, process_mail_out from plugin.base.mail.mixins import MailMixin from plugin.base.supplier import helpers as supplier from plugin.base.supplier.mixins import SupplierMixin from plugin.base.ui.mixins import UserInterfaceMixin +from plugin.events import trigger_event __all__ = [ 'APICallMixin', @@ -52,5 +58,10 @@ __all__ = [ 'UserInterfaceMixin', 'ValidationMixin', 'WellKnownMixin', + 'get_supported_barcode_model_codes_map', + 'get_supported_barcode_models', + 'process_mail_in', + 'process_mail_out', 'supplier', + 'trigger_event', ] diff --git a/src/backend/InvenTree/plugin/models.py b/src/backend/InvenTree/plugin/models.py index adc17d9f29..86704bd245 100644 --- a/src/backend/InvenTree/plugin/models.py +++ b/src/backend/InvenTree/plugin/models.py @@ -14,9 +14,8 @@ from django.utils.translation import gettext_lazy as _ import common.models import InvenTree.models import plugin.staticfiles -from plugin import InvenTreePlugin +from plugin import InvenTreePlugin, registry from plugin.events import PluginEvents, trigger_event -from plugin.registry import registry class PluginConfig(InvenTree.models.MetadataMixin, models.Model): diff --git a/src/backend/InvenTree/plugin/samples/event/test_event_sample.py b/src/backend/InvenTree/plugin/samples/event/test_event_sample.py index 035c67f2bc..3ad65cd68e 100644 --- a/src/backend/InvenTree/plugin/samples/event/test_event_sample.py +++ b/src/backend/InvenTree/plugin/samples/event/test_event_sample.py @@ -3,10 +3,8 @@ from django.test import TestCase from common.models import InvenTreeSetting -from plugin import InvenTreePlugin, registry -from plugin.base.event.events import trigger_event -from plugin.helpers import MixinNotImplementedError -from plugin.mixins import EventMixin +from plugin import InvenTreePlugin, MixinNotImplementedError, registry +from plugin.mixins import EventMixin, trigger_event class EventPluginSampleTests(TestCase): diff --git a/src/backend/InvenTree/plugin/samples/event/test_filtered_event_sample.py b/src/backend/InvenTree/plugin/samples/event/test_filtered_event_sample.py index 3a8e7474b1..8854ceae47 100644 --- a/src/backend/InvenTree/plugin/samples/event/test_filtered_event_sample.py +++ b/src/backend/InvenTree/plugin/samples/event/test_filtered_event_sample.py @@ -4,7 +4,7 @@ from django.test import TestCase from common.models import InvenTreeSetting from plugin import registry -from plugin.base.event.events import trigger_event +from plugin.mixins import trigger_event class FilteredEventPluginSampleTests(TestCase): diff --git a/src/backend/InvenTree/plugin/samples/icons/icon_sample.py b/src/backend/InvenTree/plugin/samples/icons/icon_sample.py index 795f147ebf..ff595aec9b 100644 --- a/src/backend/InvenTree/plugin/samples/icons/icon_sample.py +++ b/src/backend/InvenTree/plugin/samples/icons/icon_sample.py @@ -3,8 +3,8 @@ from django.templatetags.static import static from common.icons import IconPack -from plugin.base.icons.mixins import IconPackMixin -from plugin.plugin import InvenTreePlugin +from plugin import InvenTreePlugin +from plugin.mixins import IconPackMixin class SampleIconPlugin(IconPackMixin, InvenTreePlugin): diff --git a/src/backend/InvenTree/plugin/samples/icons/test_icon_sample.py b/src/backend/InvenTree/plugin/samples/icons/test_icon_sample.py index f1767b5278..cd1fd707b9 100644 --- a/src/backend/InvenTree/plugin/samples/icons/test_icon_sample.py +++ b/src/backend/InvenTree/plugin/samples/icons/test_icon_sample.py @@ -3,8 +3,7 @@ from django.urls import reverse from InvenTree.unit_test import InvenTreeAPITestCase -from plugin import InvenTreePlugin, registry -from plugin.helpers import MixinNotImplementedError +from plugin import InvenTreePlugin, MixinNotImplementedError, registry from plugin.mixins import IconPackMixin diff --git a/src/backend/InvenTree/plugin/samples/integration/test_allocate_sample.py b/src/backend/InvenTree/plugin/samples/integration/test_allocate_sample.py index c0e92a222d..ec2cf2d46b 100644 --- a/src/backend/InvenTree/plugin/samples/integration/test_allocate_sample.py +++ b/src/backend/InvenTree/plugin/samples/integration/test_allocate_sample.py @@ -5,7 +5,7 @@ from company.models import Company from InvenTree.unit_test import InvenTreeTestCase from order.models import SalesOrder, SalesOrderLineItem from part.models import BomItem, Part -from plugin.registry import registry +from plugin import registry from stock.models import StockItem diff --git a/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py b/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py index 3252931771..c83c0f278e 100644 --- a/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py +++ b/src/backend/InvenTree/plugin/samples/integration/test_scheduled_task.py @@ -7,10 +7,13 @@ from django.db import connection from django.db.models.query import QuerySet from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature -from plugin import InvenTreePlugin -from plugin.helpers import MixinImplementationError +from plugin import ( + InvenTreePlugin, + MixinImplementationError, + call_plugin_function, + registry, +) from plugin.mixins import ScheduleMixin -from plugin.registry import call_plugin_function, registry class ExampleScheduledTaskPluginTests(TestCase): diff --git a/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py b/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py index d48bceb924..4a96d47105 100644 --- a/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py +++ b/src/backend/InvenTree/plugin/samples/integration/test_simpleactionplugin.py @@ -1,7 +1,7 @@ """Unit tests for action plugins.""" from InvenTree.unit_test import InvenTreeTestCase -from plugin.registry import registry +from plugin import registry from plugin.samples.integration.simpleactionplugin import SimpleActionPlugin diff --git a/src/backend/InvenTree/plugin/samples/integration/test_validation_sample.py b/src/backend/InvenTree/plugin/samples/integration/test_validation_sample.py index 485356cc2f..5572ba4d13 100644 --- a/src/backend/InvenTree/plugin/samples/integration/test_validation_sample.py +++ b/src/backend/InvenTree/plugin/samples/integration/test_validation_sample.py @@ -6,7 +6,7 @@ from django.urls import reverse import build.models import part.models from InvenTree.unit_test import InvenTreeAPITestCase, InvenTreeTestCase -from plugin.registry import registry +from plugin import registry class SampleValidatorPluginTest(InvenTreeAPITestCase, InvenTreeTestCase): diff --git a/src/backend/InvenTree/plugin/samples/locate/test_locate_sample.py b/src/backend/InvenTree/plugin/samples/locate/test_locate_sample.py index 4bcb86a97a..fb02b5c640 100644 --- a/src/backend/InvenTree/plugin/samples/locate/test_locate_sample.py +++ b/src/backend/InvenTree/plugin/samples/locate/test_locate_sample.py @@ -3,8 +3,7 @@ from django.urls import reverse from InvenTree.unit_test import InvenTreeAPITestCase -from plugin import InvenTreePlugin, registry -from plugin.helpers import MixinNotImplementedError +from plugin import InvenTreePlugin, MixinNotImplementedError, registry from plugin.mixins import LocateMixin diff --git a/src/backend/InvenTree/plugin/samples/machines/sample_printer.py b/src/backend/InvenTree/plugin/samples/machines/sample_printer.py index 10e6f51d12..9ed9b2a702 100644 --- a/src/backend/InvenTree/plugin/samples/machines/sample_printer.py +++ b/src/backend/InvenTree/plugin/samples/machines/sample_printer.py @@ -6,9 +6,8 @@ from django.db import models import structlog -from machine.machine_type import BaseDriver from plugin import InvenTreePlugin -from plugin.machine import BaseMachineType +from plugin.machine import BaseDriver, BaseMachineType from plugin.machine.machine_types import LabelPrinterBaseDriver, LabelPrinterMachine from plugin.mixins import MachineDriverMixin, SettingsMixin from report.models import LabelTemplate diff --git a/src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py b/src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py index 25eb0b5a60..9c4127ef48 100644 --- a/src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py +++ b/src/backend/InvenTree/plugin/samples/mail/test_mail_sample.py @@ -3,11 +3,8 @@ from django.test import TestCase from common.models import InvenTreeSetting -from plugin import InvenTreePlugin -from plugin.base.mail.mail import process_mail_in, process_mail_out -from plugin.helpers import MixinNotImplementedError -from plugin.mixins import MailMixin -from plugin.registry import registry +from plugin import InvenTreePlugin, MixinNotImplementedError, registry +from plugin.mixins import MailMixin, process_mail_in, process_mail_out class MailPluginSampleTests(TestCase): diff --git a/src/backend/InvenTree/plugin/samples/supplier/supplier_sample.py b/src/backend/InvenTree/plugin/samples/supplier/supplier_sample.py index 4500da8c7c..89806ecce6 100644 --- a/src/backend/InvenTree/plugin/samples/supplier/supplier_sample.py +++ b/src/backend/InvenTree/plugin/samples/supplier/supplier_sample.py @@ -4,8 +4,8 @@ from django.conf import settings from company.models import Company, ManufacturerPart, SupplierPart, SupplierPriceBreak from part.models import Part +from plugin import InvenTreePlugin from plugin.mixins import SupplierMixin, supplier -from plugin.plugin import InvenTreePlugin class SampleSupplierPlugin(SupplierMixin, InvenTreePlugin): diff --git a/src/backend/InvenTree/plugin/serializers.py b/src/backend/InvenTree/plugin/serializers.py index 1b77b6063f..fd63809c1f 100644 --- a/src/backend/InvenTree/plugin/serializers.py +++ b/src/backend/InvenTree/plugin/serializers.py @@ -156,7 +156,7 @@ class PluginConfigInstallSerializer(serializers.Serializer): def save(self): """Install a plugin from a package registry and set operational results as instance data.""" - from plugin.installer import install_plugin + from plugin._internal.installer import install_plugin data = self.validated_data @@ -210,7 +210,7 @@ class PluginReloadSerializer(serializers.Serializer): def save(self): """Reload the plugin registry.""" - from plugin.registry import registry + from plugin import registry registry.reload_plugins( full_reload=self.validated_data.get('full_reload', False), @@ -267,7 +267,7 @@ class PluginUninstallSerializer(serializers.Serializer): def update(self, instance, validated_data): """Uninstall the specified plugin.""" - from plugin.installer import uninstall_plugin + from plugin._internal.installer import uninstall_plugin user = self.context['request'].user diff --git a/src/backend/InvenTree/plugin/staticfiles.py b/src/backend/InvenTree/plugin/staticfiles.py index 7342e4bb28..aa852ae227 100644 --- a/src/backend/InvenTree/plugin/staticfiles.py +++ b/src/backend/InvenTree/plugin/staticfiles.py @@ -20,8 +20,8 @@ from django.contrib.staticfiles.storage import staticfiles_storage import structlog -from plugin.lease import acquire_lease_blocking, release_lease -from plugin.registry import registry +from plugin import registry +from plugin._internal.lease import acquire_lease_blocking, release_lease logger = structlog.get_logger('inventree') diff --git a/src/backend/InvenTree/plugin/templatetags/plugin_extras.py b/src/backend/InvenTree/plugin/templatetags/plugin_extras.py index e8c945c11b..986de63a00 100644 --- a/src/backend/InvenTree/plugin/templatetags/plugin_extras.py +++ b/src/backend/InvenTree/plugin/templatetags/plugin_extras.py @@ -3,7 +3,7 @@ from django import template from django.urls import reverse -from plugin.registry import registry +from plugin import registry register = template.Library() diff --git a/src/backend/InvenTree/plugin/test_api.py b/src/backend/InvenTree/plugin/test_api.py index 4b37cc8c98..363b2ad266 100644 --- a/src/backend/InvenTree/plugin/test_api.py +++ b/src/backend/InvenTree/plugin/test_api.py @@ -498,7 +498,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): def test_settings(self): """Test settings endpoint for plugin.""" - from plugin.registry import registry + from plugin import registry registry.set_plugin_state('sample', True) url = reverse('api-plugin-settings', kwargs={'plugin': 'sample'}) @@ -519,8 +519,7 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): def test_plugin_filter_by_mixin(self): """Test filtering plugins by mixin.""" - from plugin import PluginMixinEnum - from plugin.registry import registry + from plugin import PluginMixinEnum, registry # Ensure we have some plugins loaded registry.reload_plugins(full_reload=True, collect=True) @@ -548,8 +547,8 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase): def test_plugin_filters(self): """Unit testing for plugin API filters.""" + from plugin import registry from plugin.models import PluginConfig - from plugin.registry import registry PluginConfig.objects.all().delete() registry.reload_plugins(full_reload=True, collect=True) @@ -709,7 +708,7 @@ class PluginLockedSettingsTest(PluginMixin, InvenTreeAPITestCase): """Activate the sample plugin and confirm it is ready.""" super().setUp() - from plugin.registry import registry + from plugin import registry registry.set_plugin_state(self.PLUGIN_SLUG, True) @@ -844,7 +843,7 @@ class PluginUnauthenticatedAccessTest(PluginMixin, InvenTreeAPITestCase): def setUp(self): """Activate sample plugin, then log out to simulate an anonymous client.""" super().setUp() - from plugin.registry import registry + from plugin import registry registry.set_plugin_state(self.PLUGIN_SLUG, True) self.client.logout() diff --git a/src/backend/InvenTree/plugin/test_plugin.py b/src/backend/InvenTree/plugin/test_plugin.py index 9ff53c4cdf..9944b312ab 100644 --- a/src/backend/InvenTree/plugin/test_plugin.py +++ b/src/backend/InvenTree/plugin/test_plugin.py @@ -17,9 +17,8 @@ from django.test import TestCase, override_settings import plugin.templatetags.plugin_extras as plugin_tags from InvenTree.unit_test import PluginRegistryMixin, TestQueryMixin -from plugin import InvenTreePlugin, PluginMixinEnum -from plugin.installer import install_plugin, update_plugins_file -from plugin.registry import registry +from plugin import InvenTreePlugin, PluginMixinEnum, registry +from plugin._internal.installer import install_plugin, update_plugins_file from plugin.samples.integration.another_sample import ( NoIntegrationPlugin, WrongIntegrationPlugin, @@ -817,7 +816,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): from django.db.utils import ProgrammingError from common.models import InvenTreeSetting - from plugin.lease import release_lease, try_acquire_lease + from plugin._internal.lease import release_lease, try_acquire_lease with mock.patch.object( InvenTreeSetting.objects, @@ -836,7 +835,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): def test_lease_acquire_release(self): """Test that try_acquire_lease / release_lease provide mutual exclusion.""" - from plugin.lease import release_lease, try_acquire_lease + from plugin._internal.lease import release_lease, try_acquire_lease key = '_test_lease_acquire_release' @@ -859,7 +858,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): from django.utils import timezone from common.settings import set_global_setting - from plugin.lease import CLAIM_LEASE, release_lease, try_acquire_lease + from plugin._internal.lease import CLAIM_LEASE, release_lease, try_acquire_lease key = '_test_lease_expiry' @@ -881,7 +880,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): """Test that acquire_lease_blocking gives up after its timeout, rather than hanging.""" import time - from plugin.lease import ( + from plugin._internal.lease import ( acquire_lease_blocking, release_lease, try_acquire_lease, @@ -916,7 +915,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): installed, and no future check would ever retry it. """ from common.settings import get_global_setting - from plugin.registry import registry + from plugin import registry hash_during_install = 'unset' @@ -933,16 +932,17 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): with ( tempfile.TemporaryDirectory() as tmpdir, - mock.patch('plugin.installer._env_plugin_hash_cache', None), + mock.patch('plugin._internal.installer._env_plugin_hash_cache', None), mock.patch( - 'plugin.installer.plugin_env_marker_path', + 'plugin._internal.installer.plugin_env_marker_path', return_value=Path(tmpdir) / '.inventree_plugins_hash', ), mock.patch( - 'plugin.installer.plugins_file_hash', return_value='test-hash-1' + 'plugin._internal.installer.plugins_file_hash', + return_value='test-hash-1', ), mock.patch( - 'plugin.installer.install_plugins_file', + 'plugin._internal.installer.install_plugins_file', side_effect=fake_install_plugins_file, ), ): @@ -960,21 +960,24 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): def test_install_plugin_file_failure_does_not_persist_hash(self): """Test that a failed install leaves the hash unset, so it is retried next time.""" from common.settings import get_global_setting, set_global_setting - from plugin.registry import registry + from plugin import registry set_global_setting('_PLUGIN_FILE_HASH', '') with ( tempfile.TemporaryDirectory() as tmpdir, - mock.patch('plugin.installer._env_plugin_hash_cache', None), + mock.patch('plugin._internal.installer._env_plugin_hash_cache', None), mock.patch( - 'plugin.installer.plugin_env_marker_path', + 'plugin._internal.installer.plugin_env_marker_path', return_value=Path(tmpdir) / '.inventree_plugins_hash', ), mock.patch( - 'plugin.installer.plugins_file_hash', return_value='test-hash-2' + 'plugin._internal.installer.plugins_file_hash', + return_value='test-hash-2', + ), + mock.patch( + 'plugin._internal.installer.install_plugins_file', return_value=False ), - mock.patch('plugin.installer.install_plugins_file', return_value=False), ): registry.install_plugin_file() @@ -982,20 +985,21 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): def test_install_plugin_file_skips_if_already_current(self): """Test that install_plugin_file() is a no-op once the hash already matches.""" - from plugin.registry import registry + from plugin import registry with ( tempfile.TemporaryDirectory() as tmpdir, - mock.patch('plugin.installer._env_plugin_hash_cache', None), + mock.patch('plugin._internal.installer._env_plugin_hash_cache', None), mock.patch( - 'plugin.installer.plugin_env_marker_path', + 'plugin._internal.installer.plugin_env_marker_path', return_value=Path(tmpdir) / '.inventree_plugins_hash', ), mock.patch( - 'plugin.installer.plugins_file_hash', return_value='test-hash-3' + 'plugin._internal.installer.plugins_file_hash', + return_value='test-hash-3', ), mock.patch( - 'plugin.installer.install_plugins_file', return_value=True + 'plugin._internal.installer.install_plugins_file', return_value=True ) as mock_install, ): registry.install_plugin_file() @@ -1015,7 +1019,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): previous environment's successful install of the exact same file. """ from common.settings import set_global_setting - from plugin.registry import registry + from plugin import registry # Simulate a database that already believes this hash is installed - # as if a *previous* (now-replaced) environment installed it @@ -1023,17 +1027,18 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): with ( tempfile.TemporaryDirectory() as tmpdir, - mock.patch('plugin.installer._env_plugin_hash_cache', None), + mock.patch('plugin._internal.installer._env_plugin_hash_cache', None), mock.patch( - 'plugin.installer.plugin_env_marker_path', + 'plugin._internal.installer.plugin_env_marker_path', # A fresh environment has no marker file at all return_value=Path(tmpdir) / '.inventree_plugins_hash', ), mock.patch( - 'plugin.installer.plugins_file_hash', return_value='test-hash-4' + 'plugin._internal.installer.plugins_file_hash', + return_value='test-hash-4', ), mock.patch( - 'plugin.installer.install_plugins_file', return_value=True + 'plugin._internal.installer.install_plugins_file', return_value=True ) as mock_install, ): registry.install_plugin_file() @@ -1057,7 +1062,7 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): process start. """ from common.settings import set_global_setting - from plugin.registry import registry + from plugin import registry set_global_setting('_PLUGIN_FILE_HASH', '') @@ -1066,13 +1071,16 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): marker.write_text.side_effect = OSError('Read-only file system') with ( - mock.patch('plugin.installer._env_plugin_hash_cache', None), - mock.patch('plugin.installer.plugin_env_marker_path', return_value=marker), + mock.patch('plugin._internal.installer._env_plugin_hash_cache', None), mock.patch( - 'plugin.installer.plugins_file_hash', return_value='test-hash-5' + 'plugin._internal.installer.plugin_env_marker_path', return_value=marker ), mock.patch( - 'plugin.installer.install_plugins_file', return_value=True + 'plugin._internal.installer.plugins_file_hash', + return_value='test-hash-5', + ), + mock.patch( + 'plugin._internal.installer.install_plugins_file', return_value=True ) as mock_install, ): registry.install_plugin_file() @@ -1085,8 +1093,8 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): def test_builtin_mandatory_plugins(self): """Test that mandatory builtin plugins are always loaded.""" + from plugin import registry from plugin.models import PluginConfig - from plugin.registry import registry # Start with a 'clean slate' PluginConfig.objects.all().delete() @@ -1131,8 +1139,8 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): def test_mandatory_plugins(self): """Test that plugins marked as 'mandatory' are always active.""" + from plugin import registry from plugin.models import PluginConfig - from plugin.registry import registry # Start with a 'clean slate' PluginConfig.objects.all().delete() @@ -1187,8 +1195,8 @@ class RegistryTests(TestQueryMixin, PluginRegistryMixin, TestCase): def test_with_mixin(self): """Tests for the 'with_mixin' registry method.""" + from plugin import registry from plugin.models import PluginConfig - from plugin.registry import registry self.ensurePluginsLoaded() diff --git a/src/backend/InvenTree/plugin/test_staticfiles.py b/src/backend/InvenTree/plugin/test_staticfiles.py index 8ab3b59fe2..d1347602ea 100644 --- a/src/backend/InvenTree/plugin/test_staticfiles.py +++ b/src/backend/InvenTree/plugin/test_staticfiles.py @@ -14,8 +14,8 @@ from django.test import TestCase import plugin.staticfiles as plugin_staticfiles from InvenTree.unit_test import PluginRegistryMixin -from plugin.lease import try_acquire_lease -from plugin.registry import registry +from plugin import registry +from plugin._internal.lease import try_acquire_lease class FakePlugin: diff --git a/src/backend/InvenTree/plugin/testing/label_machines.py b/src/backend/InvenTree/plugin/testing/label_machines.py index 54967d87a7..31281b460a 100644 --- a/src/backend/InvenTree/plugin/testing/label_machines.py +++ b/src/backend/InvenTree/plugin/testing/label_machines.py @@ -3,9 +3,8 @@ import structlog from rest_framework import serializers -from machine.machine_type import BaseDriver from plugin import InvenTreePlugin -from plugin.machine import BaseMachineType +from plugin.machine import BaseDriver, BaseMachineType from plugin.machine.machine_types import LabelPrinterBaseDriver from plugin.mixins import MachineDriverMixin, SettingsMixin diff --git a/src/backend/InvenTree/plugin/urls.py b/src/backend/InvenTree/plugin/urls.py index b52921040a..7fac893f9b 100644 --- a/src/backend/InvenTree/plugin/urls.py +++ b/src/backend/InvenTree/plugin/urls.py @@ -15,7 +15,7 @@ PLUGIN_BASE = 'plugin' # Constant for links def get_plugin_urls(): """Returns a urlpattern that can be integrated into the global urls.""" - from plugin.registry import registry + from plugin import registry urls = [] @@ -60,7 +60,7 @@ def get_plugin_urls(): def wellknownindexview(request): """Simple view that returns a list of all well-known URLs as JSON.""" - from plugin.registry import registry + from plugin import registry well_known_urls = {} if registry.is_ready: @@ -78,7 +78,7 @@ def wellknownindexview(request): def get_wellknown_urls(): """Returns a urlpattern that can be integrated into the global urls (as redirects).""" - from plugin.registry import registry + from plugin import registry urls = [] diff --git a/src/backend/InvenTree/report/models.py b/src/backend/InvenTree/report/models.py index 8ba02e69d1..8b6bd54d65 100644 --- a/src/backend/InvenTree/report/models.py +++ b/src/backend/InvenTree/report/models.py @@ -33,8 +33,7 @@ from common.settings import get_global_setting from company.models import Company from InvenTree.helpers_model import get_base_url from InvenTree.models import MetadataMixin -from plugin import InvenTreePlugin, PluginMixinEnum -from plugin.registry import registry +from plugin import InvenTreePlugin, PluginMixinEnum, registry from report.security import restricted_db_access try: diff --git a/src/backend/InvenTree/report/tasks.py b/src/backend/InvenTree/report/tasks.py index 8a1a6b4ae6..4165751b14 100644 --- a/src/backend/InvenTree/report/tasks.py +++ b/src/backend/InvenTree/report/tasks.py @@ -94,7 +94,7 @@ def print_labels( and will continuously update the status of the DataOutput object. """ from common.models import DataOutput - from plugin.registry import registry + from plugin import registry from report.models import LabelTemplate try: diff --git a/src/backend/InvenTree/report/tests.py b/src/backend/InvenTree/report/tests.py index d005d82493..d68d226209 100644 --- a/src/backend/InvenTree/report/tests.py +++ b/src/backend/InvenTree/report/tests.py @@ -32,7 +32,7 @@ from InvenTree.template import InvenTreeStorageTemplateLoader from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase from order.models import PurchaseOrder, ReturnOrder, SalesOrder from part.models import Part -from plugin.registry import registry +from plugin import registry from report.models import LabelTemplate, ReportTemplate from report.tasks import print_labels from stock.models import StockItem, StockLocation diff --git a/src/backend/InvenTree/stock/tests.py b/src/backend/InvenTree/stock/tests.py index 7a263ebb66..d4b3d2a5fb 100644 --- a/src/backend/InvenTree/stock/tests.py +++ b/src/backend/InvenTree/stock/tests.py @@ -1371,7 +1371,7 @@ class StockBarcodeTest(StockTestBase): def test_location_barcode_basics(self): """Simple tests for the StockLocation barcode integration.""" # Set the barcode plugin to use the legacy barcode format - from plugin.registry import registry + from plugin import registry plugin = registry.get_plugin('inventreebarcode') @@ -1760,7 +1760,7 @@ class TestResultTest(StockTestBase): """Test duplicate item behaviour.""" # Create an example stock item by copying one from the database (because we are lazy) - from plugin.registry import registry + from plugin import registry item = StockItem.objects.get(pk=522)