From d3d0b76c581a702b6ce436f839f73d893ded42f1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 28 May 2022 03:04:48 +0200 Subject: [PATCH] fix docstrings 7 --- InvenTree/plugin/api.py | 25 +++---- InvenTree/plugin/events.py | 4 +- InvenTree/plugin/helpers.py | 62 ++++++----------- InvenTree/plugin/models.py | 51 ++++---------- InvenTree/plugin/plugin.py | 125 +++++++++-------------------------- InvenTree/plugin/registry.py | 73 +++++++------------- 6 files changed, 98 insertions(+), 242 deletions(-) diff --git a/InvenTree/plugin/api.py b/InvenTree/plugin/api.py index cfdd08d51b..c9a8f39190 100644 --- a/InvenTree/plugin/api.py +++ b/InvenTree/plugin/api.py @@ -1,6 +1,4 @@ -""" -JSON API for the plugin app -""" +"""JSON API for the plugin app""" from django.conf import settings from django.urls import include, re_path @@ -20,7 +18,7 @@ from plugin.registry import registry class PluginList(generics.ListAPIView): - """ API endpoint for list of PluginConfig objects + """API endpoint for list of PluginConfig objects - GET: Return a list of all PluginConfig objects """ @@ -79,7 +77,7 @@ class PluginList(generics.ListAPIView): class PluginDetail(generics.RetrieveUpdateDestroyAPIView): - """ API detail endpoint for PluginConfig object + """API detail endpoint for PluginConfig object get: Return a single PluginConfig object @@ -96,9 +94,8 @@ class PluginDetail(generics.RetrieveUpdateDestroyAPIView): class PluginInstall(generics.CreateAPIView): - """ - Endpoint for installing a new plugin - """ + """Endpoint for installing a new plugin""" + queryset = PluginConfig.objects.none() serializer_class = PluginSerializers.PluginConfigInstallSerializer @@ -115,8 +112,7 @@ class PluginInstall(generics.CreateAPIView): class PluginSettingList(generics.ListAPIView): - """ - List endpoint for all plugin related settings. + """List endpoint for all plugin related settings. - read only - only accessible by staff users @@ -140,8 +136,7 @@ class PluginSettingList(generics.ListAPIView): class PluginSettingDetail(generics.RetrieveUpdateAPIView): - """ - Detail endpoint for a plugin-specific setting. + """Detail endpoint for a plugin-specific setting. Note that these cannot be created or deleted via the API """ @@ -150,13 +145,11 @@ class PluginSettingDetail(generics.RetrieveUpdateAPIView): serializer_class = PluginSerializers.PluginSettingSerializer def get_object(self): - """ - Lookup the plugin setting object, based on the URL. - The URL provides the 'slug' of the plugin, and the 'key' of the setting. + """Lookup the plugin setting object, based on the URL. + The URL provides the 'slug' of the plugin, and the 'key' of the setting. Both the 'slug' and 'key' must be valid, else a 404 error is raised """ - plugin_slug = self.kwargs['plugin'] key = self.kwargs['key'] diff --git a/InvenTree/plugin/events.py b/InvenTree/plugin/events.py index 452e71d4ef..eb8c9f8050 100644 --- a/InvenTree/plugin/events.py +++ b/InvenTree/plugin/events.py @@ -1,6 +1,4 @@ -""" -Import helper for events -""" +"""Import helper for events""" from plugin.base.event.events import (process_event, register_event, trigger_event) diff --git a/InvenTree/plugin/helpers.py b/InvenTree/plugin/helpers.py index 8a3a38a4ca..79eba76067 100644 --- a/InvenTree/plugin/helpers.py +++ b/InvenTree/plugin/helpers.py @@ -1,6 +1,5 @@ -""" -Helpers for plugin app -""" +"""Helpers for plugin app""" + import inspect import logging import os @@ -20,9 +19,8 @@ logger = logging.getLogger('inventree') # region logging / errors class IntegrationPluginError(Exception): - """ - Error that encapsulates another error and adds the path / reference of the raising plugin - """ + """Error that encapsulates another error and adds the path / reference of the raising plugin""" + def __init__(self, path, message): self.path = path self.message = message @@ -32,24 +30,20 @@ class IntegrationPluginError(Exception): class MixinImplementationError(ValueError): - """ - Error if mixin was implemented wrong in plugin + """Error if mixin was implemented wrong in plugin + Mostly raised if constant is missing """ pass class MixinNotImplementedError(NotImplementedError): - """ - Error if necessary mixin function was not overwritten - """ + """Error if necessary mixin function was not overwritten""" pass def log_error(error, reference: str = 'general'): - """ - Log an plugin error - """ + """Log an plugin error""" from plugin import registry # make sure the registry is set up @@ -61,9 +55,7 @@ def log_error(error, reference: str = 'general'): def handle_error(error, do_raise: bool = True, do_log: bool = True, log_name: str = ''): - """ - Handles an error and casts it as an IntegrationPluginError - """ + """Handles an error and casts it as an IntegrationPluginError""" package_path = traceback.extract_tb(error.__traceback__)[-1].filename install_path = sysconfig.get_paths()["purelib"] try: @@ -99,9 +91,7 @@ def handle_error(error, do_raise: bool = True, do_log: bool = True, log_name: st # region git-helpers def get_git_log(path): - """ - Get dict with info of the last commit to file named in path - """ + """Get dict with info of the last commit to file named in path""" from plugin import registry output = None @@ -122,8 +112,7 @@ def get_git_log(path): def check_git_version(): - """returns if the current git version supports modern features""" - + """Returns if the current git version supports modern features""" # get version string try: output = str(subprocess.check_output(['git', '--version'], cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8') @@ -143,13 +132,11 @@ def check_git_version(): class GitStatus: - """ - Class for resolving git gpg singing state - """ + """Class for resolving git gpg singing state""" + class Definition: - """ - Definition of a git gpg sing state - """ + """Definition of a git gpg sing state""" + key: str = 'N' status: int = 2 msg: str = '' @@ -172,8 +159,7 @@ class GitStatus: # region plugin finders def get_modules(pkg): - """get all modules in a package""" - + """Get all modules in a package""" context = {} for loader, name, ispkg in pkgutil.walk_packages(pkg.__path__): try: @@ -195,18 +181,16 @@ def get_modules(pkg): def get_classes(module): - """get all classes in a given module""" + """Get all classes in a given module""" return inspect.getmembers(module, inspect.isclass) def get_plugins(pkg, baseclass): - """ - Return a list of all modules under a given package. + """Return a list of all modules under a given package. - Modules must be a subclass of the provided 'baseclass' - Modules must have a non-empty NAME parameter """ - plugins = [] modules = get_modules(pkg) @@ -225,10 +209,7 @@ def get_plugins(pkg, baseclass): # region templates def render_template(plugin, template_file, context=None): - """ - Locate and render a template file, available in the global template context. - """ - + """Locate and render a template file, available in the global template context.""" try: tmp = template.loader.get_template(template_file) except template.TemplateDoesNotExist: @@ -247,10 +228,7 @@ def render_template(plugin, template_file, context=None): def render_text(text, context=None): - """ - Locate a raw string with provided context - """ - + """Locate a raw string with provided context""" ctx = template.Context(context) return template.Template(text).render(ctx) diff --git a/InvenTree/plugin/models.py b/InvenTree/plugin/models.py index 35becb9c47..a5222a3d3e 100644 --- a/InvenTree/plugin/models.py +++ b/InvenTree/plugin/models.py @@ -1,6 +1,4 @@ -""" -Plugin model definitions -""" +"""Plugin model definitions""" import warnings @@ -14,8 +12,7 @@ from plugin import InvenTreePlugin, registry class MetadataMixin(models.Model): - """ - Model mixin class which adds a JSON metadata field to a model, + """Model mixin class which adds a JSON metadata field to a model, for use by any (and all) plugins. The intent of this mixin is to provide a metadata field on a model instance, @@ -37,8 +34,7 @@ class MetadataMixin(models.Model): ) def get_metadata(self, key: str, backup_value=None): - """ - Finds metadata for this model instance, using the provided key for lookup + """Finds metadata for this model instance, using the provided key for lookup Args: key: String key for requesting metadata. e.g. if a plugin is accessing the metadata, the plugin slug should be used @@ -46,22 +42,19 @@ class MetadataMixin(models.Model): Returns: Python dict object containing requested metadata. If no matching metadata is found, returns None """ - if self.metadata is None: return backup_value return self.metadata.get(key, backup_value) def set_metadata(self, key: str, data, commit=True): - """ - Save the provided metadata under the provided key. + """Save the provided metadata under the provided key. Args: key: String key for saving metadata data: Data object to save - must be able to be rendered as a JSON string overwrite: If true, existing metadata with the provided key will be overwritten. If false, a merge will be attempted """ - if self.metadata is None: # Handle a null field value self.metadata = {} @@ -73,14 +66,14 @@ class MetadataMixin(models.Model): class PluginConfig(models.Model): - """ - A PluginConfig object holds settings for plugins. + """A PluginConfig object holds settings for plugins. Attributes: key: slug of the plugin (this must be unique across all installed plugins!) name: PluginName of the plugin - serves for a manual double check if the right plugin is used active: Should the plugin be loaded? """ + class Meta: verbose_name = _("Plugin Configuration") verbose_name_plural = _("Plugin Configurations") @@ -123,10 +116,7 @@ class PluginConfig(models.Model): # functions def __init__(self, *args, **kwargs): - """ - Override to set original state of the plugin-config instance - """ - + """Override to set original state of the plugin-config instance""" super().__init__(*args, **kwargs) self.__org_active = self.active @@ -145,9 +135,7 @@ class PluginConfig(models.Model): } def save(self, force_insert=False, force_update=False, *args, **kwargs): - """ - Extend save method to reload plugins if the 'active' status changes - """ + """Extend save method to reload plugins if the 'active' status changes""" reload = kwargs.pop('no_reload', False) # check if no_reload flag is set ret = super().save(force_insert, force_update, *args, **kwargs) @@ -163,9 +151,7 @@ class PluginConfig(models.Model): class PluginSetting(common.models.BaseInvenTreeSetting): - """ - This model represents settings for individual plugins - """ + """This model represents settings for individual plugins""" class Meta: unique_together = [ @@ -182,8 +168,7 @@ class PluginSetting(common.models.BaseInvenTreeSetting): @classmethod def get_setting_definition(cls, key, **kwargs): - """ - In the BaseInvenTreeSetting class, we have a class attribute named 'SETTINGS', + """In the BaseInvenTreeSetting class, we have a class attribute named 'SETTINGS', which is a dict object that fully defines all the setting parameters. Here, unlike the BaseInvenTreeSetting, we do not know the definitions of all settings @@ -209,20 +194,14 @@ class PluginSetting(common.models.BaseInvenTreeSetting): return super().get_setting_definition(key, **kwargs) def get_kwargs(self): - """ - Explicit kwargs required to uniquely identify a particular setting object, - in addition to the 'key' parameter - """ - + """Explicit kwargs required to uniquely identify a particular setting object, in addition to the 'key' parameter""" return { 'plugin': self.plugin, } class NotificationUserSetting(common.models.BaseInvenTreeSetting): - """ - This model represents notification settings for a user - """ + """This model represents notification settings for a user""" class Meta: unique_together = [ @@ -238,11 +217,7 @@ class NotificationUserSetting(common.models.BaseInvenTreeSetting): return super().get_setting_definition(key, **kwargs) def get_kwargs(self): - """ - Explicit kwargs required to uniquely identify a particular setting object, - in addition to the 'key' parameter - """ - + """Explicit kwargs required to uniquely identify a particular setting object, in addition to the 'key' parameter""" return { 'method': self.method, 'user': self.user, diff --git a/InvenTree/plugin/plugin.py b/InvenTree/plugin/plugin.py index d5f6a7bccf..a22ad156a3 100644 --- a/InvenTree/plugin/plugin.py +++ b/InvenTree/plugin/plugin.py @@ -1,7 +1,5 @@ -# -*- coding: utf-8 -*- -""" -Base Class for InvenTree plugins -""" +"""Base Class for InvenTree plugins""" + import inspect import logging import os @@ -55,24 +53,18 @@ class MetaBase: return value def plugin_name(self): - """ - Name of plugin - """ + """Name of plugin""" return self.get_meta_value('NAME', 'PLUGIN_NAME') @property def name(self): - """ - Name of plugin - """ + """Name of plugin""" return self.plugin_name() def plugin_slug(self): - """ - Slug of plugin - If not set plugin name slugified - """ + """Slug of plugin + If not set plugin name slugified""" slug = self.get_meta_value('SLUG', 'PLUGIN_SLUG', None) if not slug: slug = self.plugin_name() @@ -81,16 +73,11 @@ class MetaBase: @property def slug(self): - """ - Slug of plugin - """ + """Slug of plugin""" return self.plugin_slug() def plugin_title(self): - """ - Title of plugin - """ - + """Title of plugin""" title = self.get_meta_value('TITLE', 'PLUGIN_TITLE', None) if title: return title @@ -98,16 +85,11 @@ class MetaBase: @property def human_name(self): - """ - Human readable name of plugin - """ + """Human readable name of plugin""" return self.plugin_title() def plugin_config(self): - """ - Return the PluginConfig object associated with this plugin - """ - + """Return the PluginConfig object associated with this plugin""" try: import plugin.models @@ -121,10 +103,7 @@ class MetaBase: return cfg def is_active(self): - """ - Return True if this plugin is currently active - """ - + """Return True if this plugin is currently active""" cfg = self.plugin_config() if cfg: @@ -134,9 +113,7 @@ class MetaBase: class MixinBase: - """ - Base set of mixin functions and mechanisms - """ + """Base set of mixin functions and mechanisms""" def __init__(self, *args, **kwargs) -> None: self._mixinreg = {} @@ -144,15 +121,11 @@ class MixinBase: super().__init__(*args, **kwargs) def mixin(self, key): - """ - Check if mixin is registered - """ + """Check if mixin is registered""" return key in self._mixins def mixin_enabled(self, key): - """ - Check if mixin is registered, enabled and ready - """ + """Check if mixin is registered, enabled and ready""" if self.mixin(key): fnc_name = self._mixins.get(key) @@ -164,18 +137,12 @@ class MixinBase: return False def add_mixin(self, key: str, fnc_enabled=True, cls=None): - """ - Add a mixin to the plugins registry - """ - + """Add a mixin to the plugins registry""" self._mixins[key] = fnc_enabled self.setup_mixin(key, cls=cls) def setup_mixin(self, key, cls=None): - """ - Define mixin details for the current mixin -> provides meta details for all active mixins - """ - + """Define mixin details for the current mixin -> provides meta details for all active mixins""" # get human name human_name = getattr(cls.MixinMeta, 'MIXIN_NAME', key) if cls and hasattr(cls, 'MixinMeta') else key @@ -187,10 +154,7 @@ class MixinBase: @property 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) if mixins: # filter out base @@ -202,8 +166,7 @@ class MixinBase: class InvenTreePlugin(MixinBase, MetaBase): - """ - The InvenTreePlugin class is used to integrate with 3rd party software + """The InvenTreePlugin class is used to integrate with 3rd party software DO NOT USE THIS DIRECTLY, USE plugin.InvenTreePlugin """ @@ -226,9 +189,7 @@ class InvenTreePlugin(MixinBase, MetaBase): # region properties @property def description(self): - """ - Description of plugin - """ + """Description of plugin""" description = getattr(self, 'DESCRIPTION', None) if not description: description = self.plugin_name() @@ -236,9 +197,7 @@ class InvenTreePlugin(MixinBase, MetaBase): @property def author(self): - """ - Author of plugin - either from plugin settings or git - """ + """Author of plugin - either from plugin settings or git""" author = getattr(self, 'AUTHOR', None) if not author: author = self.package.get('author') @@ -248,9 +207,7 @@ class InvenTreePlugin(MixinBase, MetaBase): @property def pub_date(self): - """ - Publishing date of plugin - either from plugin settings or git - """ + """Publishing date of plugin - either from plugin settings or git""" pub_date = getattr(self, 'PUBLISH_DATE', None) if not pub_date: pub_date = self.package.get('date') @@ -262,77 +219,57 @@ class InvenTreePlugin(MixinBase, MetaBase): @property def version(self): - """ - Version of plugin - """ + """Version of plugin""" version = getattr(self, 'VERSION', None) return version @property def website(self): - """ - Website of plugin - if set else None - """ + """Website of plugin - if set else None""" website = getattr(self, 'WEBSITE', None) return website @property def license(self): - """ - License of plugin - """ + """License of plugin""" lic = getattr(self, 'LICENSE', None) return lic # endregion @property def _is_package(self): - """ - Is the plugin delivered as a package - """ + """Is the plugin delivered as a package""" return getattr(self, 'is_package', False) @property def is_sample(self): - """ - Is this plugin part of the samples? - """ + """Is this plugin part of the samples?""" path = str(self.package_path) return path.startswith('plugin/samples/') @property def package_path(self): - """ - Path to the plugin - """ + """Path to the plugin""" if self._is_package: return self.__module__ # pragma: no cover return pathlib.Path(self.def_path).relative_to(settings.BASE_DIR) @property def settings_url(self): - """ - URL to the settings panel for this plugin - """ + """URL to the settings panel for this plugin""" return f'{reverse("settings")}#select-plugin-{self.slug}' # region package info def _get_package_commit(self): - """ - Get last git commit for the plugin - """ + """Get last git commit for the plugin""" return get_git_log(self.def_path) def _get_package_metadata(self): - """ - Get package metadata for plugin - """ + """Get package metadata for plugin""" return {} # pragma: no cover # TODO add usage for package metadata def define_package(self): - """ - Add package info of the plugin into plugins context - """ + """Add package info of the plugin into plugins context""" package = self._get_package_metadata() if self._is_package else self._get_package_commit() # process date diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 2f5e28bb72..2dcb796d59 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -30,9 +30,7 @@ logger = logging.getLogger('inventree') class PluginsRegistry: - """ - The PluginsRegistry class - """ + """The PluginsRegistry class""" def __init__(self) -> None: # plugin registry @@ -54,10 +52,7 @@ class PluginsRegistry: self.mixins_settings = {} def get_plugin(self, slug): - """ - Lookup plugin by slug (unique key). - """ - + """Lookup plugin by slug (unique key).""" if slug not in self.plugins: logger.warning(f"Plugin registry has no record of plugin '{slug}'") return None @@ -65,15 +60,13 @@ class PluginsRegistry: return self.plugins[slug] def call_plugin_function(self, slug, func, *args, **kwargs): - """ - Call a member function (named by 'func') of the plugin named by 'slug'. + """Call a member function (named by 'func') of the plugin named by 'slug'. As this is intended to be run by the background worker, we do not perform any try/except here. Instead, any error messages are returned to the worker. """ - plugin = self.get_plugin(slug) if not plugin: @@ -86,9 +79,7 @@ class PluginsRegistry: # region public functions # region loading / unloading def load_plugins(self): - """ - Load and activate all IntegrationPlugins - """ + """Load and activate all IntegrationPlugins""" if not settings.PLUGINS_ENABLED: # Plugins not enabled, do nothing return # pragma: no cover @@ -143,10 +134,7 @@ class PluginsRegistry: logger.info('Finished loading plugins') 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 @@ -170,10 +158,7 @@ class PluginsRegistry: logger.info('Finished unloading plugins') def reload_plugins(self): - """ - Safely reload IntegrationPlugins - """ - + """Safely reload IntegrationPlugins""" # Do not reload whe currently loading if self.is_loading: return # pragma: no cover @@ -188,7 +173,6 @@ class PluginsRegistry: def collect_plugins(self): """Collect plugins from all possible ways of loading""" - if not settings.PLUGINS_ENABLED: # Plugins not enabled, do nothing return # pragma: no cover @@ -217,10 +201,7 @@ class PluginsRegistry: logger.info(", ".join([a.__module__ for a in self.plugin_modules])) def install_plugin_file(self): - """ - Make sure all plugins are installed in the current enviroment - """ - + """Make sure all plugins are installed in the current enviroment""" if settings.PLUGIN_FILE_CHECKED: logger.info('Plugin file was already checked') return True @@ -241,9 +222,7 @@ class PluginsRegistry: # region registry functions def with_mixin(self, mixin: str, active=None): - """ - Returns reference to all plugins that have a specified mixin enabled - """ + """Returns reference to all plugins that have a specified mixin enabled""" result = [] for plugin in self.plugins.values(): @@ -264,14 +243,12 @@ class PluginsRegistry: # region general internal loading /activating / deactivating / deloading def _init_plugins(self, disabled=None): - """ - Initialise all found plugins + """Initialise all found plugins :param disabled: loading path of disabled app, defaults to None :type disabled: str, optional :raises error: IntegrationPluginError """ - from plugin.models import PluginConfig logger.info('Starting plugin initialisation') @@ -335,8 +312,7 @@ class PluginsRegistry: self.plugins_inactive[plug_key] = plugin_db_setting # pragma: no cover def _activate_plugins(self, force_reload=False): - """ - Run activation functions for all plugins + """Run activation functions for all plugins :param force_reload: force reload base apps, defaults to False :type force_reload: bool, optional @@ -351,7 +327,6 @@ class PluginsRegistry: def _deactivate_plugins(self): """Run deactivation functions for all plugins""" - self.deactivate_plugin_app() self.deactivate_plugin_schedule() self.deactivate_plugin_settings() @@ -425,15 +400,14 @@ class PluginsRegistry: logger.warning("activate_integration_schedule failed, database not ready") def deactivate_plugin_schedule(self): - """ - Deactivate ScheduleMixin + """Deactivate ScheduleMixin + currently nothing is done """ pass def activate_plugin_app(self, plugins, force_reload=False): - """ - Activate AppMixin plugins - add custom apps and reload + """Activate AppMixin plugins - add custom apps and reload :param plugins: list of IntegrationPlugins that should be installed :type plugins: dict @@ -471,9 +445,10 @@ class PluginsRegistry: self._update_urls() def _reregister_contrib_apps(self): - """fix reloading of contrib apps - models and admin - this is needed if plugins were loaded earlier and then reloaded as models and admins rely on imports - those register models and admin in their respective objects (e.g. admin.site for admin) + """Fix reloading of contrib apps - models and admin + + This is needed if plugins were loaded earlier and then reloaded as models and admins rely on imports. + Those register models and admin in their respective objects (e.g. admin.site for admin). """ for plugin_path in self.installed_apps: try: @@ -503,8 +478,9 @@ class PluginsRegistry: reload(app_config.module.admin) def _get_plugin_path(self, plugin): - """parse plugin path - the input can be eiter: + """Parse plugin path + + The input can be eiter: - a local file / dir - a package """ @@ -518,7 +494,6 @@ class PluginsRegistry: def deactivate_plugin_app(self): """Deactivate AppMixin plugins - some magic required""" - # unregister models from admin for plugin_path in self.installed_apps: models = [] # the modelrefs need to be collected as poping an item in a iter is not welcomed @@ -601,9 +576,9 @@ class PluginsRegistry: self.is_loading = False def _try_reload(self, cmd, *args, **kwargs): - """ - wrapper to try reloading the apps - throws an custom error that gets handled by the loading function + """Wrapper to try reloading the apps + + Throws an custom error that gets handled by the loading function """ try: cmd(*args, **kwargs) @@ -617,5 +592,5 @@ registry = PluginsRegistry() def call_function(plugin_name, function_name, *args, **kwargs): - """ Global helper function to call a specific member function of a plugin """ + """Global helper function to call a specific member function of a plugin""" return registry.call_plugin_function(plugin_name, function_name, *args, **kwargs)