From ca7aa322c3ef5e439a504d7c4c6343cd2ef2e7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 29 Nov 2017 15:55:33 +0100 Subject: [PATCH] Some cleanup, refactoring & docs --- src/octoprint/plugin/core.py | 7 + src/octoprint/plugin/types.py | 90 ++++++++-- .../plugins/announcements/__init__.py | 2 + src/octoprint/plugins/corewizard/__init__.py | 140 +--------------- .../plugins/corewizard/subwizards.py | 154 ++++++++++++++++++ src/octoprint/plugins/cura/__init__.py | 4 +- src/octoprint/plugins/cura/profile.py | 2 + src/octoprint/plugins/discovery/__init__.py | 21 ++- .../plugins/octopi_support/__init__.py | 1 + .../plugins/pluginmanager/__init__.py | 11 +- .../plugins/softwareupdate/__init__.py | 21 +-- 11 files changed, 284 insertions(+), 169 deletions(-) create mode 100644 src/octoprint/plugins/corewizard/subwizards.py diff --git a/src/octoprint/plugin/core.py b/src/octoprint/plugin/core.py index 9d16a2ae..2cce7fe1 100644 --- a/src/octoprint/plugin/core.py +++ b/src/octoprint/plugin/core.py @@ -1565,6 +1565,13 @@ class Plugin(object): initialization of the implementation. """ + def __init__(self): + self._identifier = None + self._plugin_name = None + self._plugin_version = None + self._basefolder = None + self._logger = None + def initialize(self): """ Called by the plugin core after performing all injections. Override this to initialize your implementation. diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index ceb8916c..b39dcac9 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -8,9 +8,11 @@ Please note that the plugin implementation types are documented in the section .. autoclass:: OctoPrintPlugin :show-inheritance: + :members: .. autoclass:: ReloadNeedingPlugin :show-inheritance: + :members: """ @@ -23,6 +25,7 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms from .core import (Plugin, RestartNeedingPlugin, SortablePlugin) +# noinspection PyCompatibility from past.builtins import basestring class OctoPrintPlugin(Plugin): @@ -74,16 +77,39 @@ class OctoPrintPlugin(Plugin): The :class:`~octoprint.server.LifecycleManager` instance. Injected by the plugin core system upon initialization of the implementation. + .. attribute:: _user_manager + + The :class:`~octoprint.users.UserManager` instance. Injected by the plugin core system upon initialization + of the implementation. + + .. attribute:: _connectivity_checker + + The :class:`~octoprint.util.ConnectivityChecker` instance. Injected by the plugin core system upon initialization + of the implementation. + .. attribute:: _data_folder Path to the data folder for the plugin to use for any data it might have to persist. Should always be accessed through :meth:`get_plugin_data_folder` since that function will also ensure that the data folder actually exists and if not creating it before returning it. Injected by the plugin core system upon initialization of the implementation. - - .. automethod:: get_plugin_data_folder """ + # noinspection PyMissingConstructor + def __init__(self): + self._plugin_manager = None + self._printer_profile_manager = None + self._event_bus = None + self._analysis_queue = None + self._slicing_manager = None + self._file_manager = None + self._printer = None + self._app_session_manager = None + self._plugin_lifecycle_manager = None + self._user_manager = None + self._connectivity_checker = None + self._data_folder = None + def get_plugin_data_folder(self): """ Retrieves the path to a data folder specifically for the plugin, ensuring it exists and if not creating it @@ -107,10 +133,10 @@ class ReloadNeedingPlugin(Plugin): class EnvironmentDetectionPlugin(OctoPrintPlugin, RestartNeedingPlugin): - + def get_additional_environment(self): pass - + def on_environment_detected(self, environment, *args, **kwargs): pass @@ -602,6 +628,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): See below for details on this. """ + # noinspection PyMethodMayBeStatic,PyUnusedLocal def will_handle_ui(self, request): """ Called by OctoPrint to determine if the mixin implementation will be @@ -624,6 +651,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return False + # noinspection PyMethodMayBeStatic,PyUnusedLocal def on_ui_render(self, now, request, render_kwargs): """ Called by OctoPrint to retrieve the response to send to the client @@ -712,6 +740,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): return None + # noinspection PyMethodMayBeStatic def get_ui_additional_key_data_for_cache(self): """ Allows to return additional data to use in the cache key. @@ -723,6 +752,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_additional_tracked_files(self): """ Allows to return additional files to track for validating existing caches. By default OctoPrint @@ -735,6 +765,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_custom_tracked_files(self): """ Allows to define a complete separate set of files to track for (in)validating the cache. If this @@ -747,6 +778,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_custom_etag(self): """ Allows to use a custom way to calculate the ETag, instead of the default method (hashing @@ -757,6 +789,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_additional_etag(self, default_additional): """ Allows to provide a list of additional fields to use for ETag generation. @@ -772,6 +805,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return default_additional + # noinspection PyMethodMayBeStatic def get_ui_custom_lastmodified(self): """ Allows to calculate the LastModified differently than using the most recent modification @@ -782,6 +816,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_preemptive_caching_enabled(self): """ Allows to control whether the view provided by the plugin should be preemptively @@ -794,6 +829,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return True + # noinspection PyMethodMayBeStatic def get_ui_data_for_preemptive_caching(self): """ Allows defining additional data to be persisted in the preemptive cache configuration, on @@ -804,6 +840,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_additional_request_data_for_preemptive_caching(self): """ Allows defining additional request data to persist in the preemptive cache configuration and @@ -819,6 +856,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return None + # noinspection PyMethodMayBeStatic def get_ui_preemptive_caching_additional_unless(self): """ Allows defining additional reasons for temporarily not adding a preemptive cache record for @@ -832,6 +870,7 @@ class UiPlugin(OctoPrintPlugin, SortablePlugin): """ return False + # noinspection PyMethodMayBeStatic def get_ui_custom_template_filter(self, default_template_filter): """ Allows to specify a custom template filter to use for filtering the template contained in the @@ -916,6 +955,7 @@ class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin): ``WizardPlugin`` is a :class:`~octoprint.plugin.core.ReloadNeedingPlugin`. """ + # noinspection PyMethodMayBeStatic def is_wizard_required(self): """ Allows the plugin to report whether it needs to display a wizard to the @@ -931,6 +971,7 @@ class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin): """ return False + # noinspection PyMethodMayBeStatic def get_wizard_version(self): """ The version of this plugin's wizard. OctoPrint will only display a wizard @@ -950,6 +991,7 @@ class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin): """ return None + # noinspection PyMethodMayBeStatic def get_wizard_details(self): """ Called by OctoPrint when the wizard wrapper dialog is shown. Allows the plugin to return data @@ -964,6 +1006,7 @@ class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin): """ return dict() + # noinspection PyMethodMayBeStatic,PyUnusedLocal def on_wizard_finish(self, handled): """ Called by OctoPrint whenever the user finishes a wizard session. @@ -982,6 +1025,7 @@ class WizardPlugin(OctoPrintPlugin, ReloadNeedingPlugin): """ pass + # noinspection PyProtectedMember @classmethod def is_wizard_ignored(cls, seen_wizards, implementation): """ @@ -1132,6 +1176,7 @@ class SimpleApiPlugin(OctoPrintPlugin): } """ + # noinspection PyMethodMayBeStatic def get_api_commands(self): """ Return a dictionary here with the keys representing the accepted commands and the values being lists of @@ -1139,12 +1184,14 @@ class SimpleApiPlugin(OctoPrintPlugin): """ return None + # noinspection PyMethodMayBeStatic def is_api_adminonly(self): """ Return True if the API is only available to users having the admin role. """ return False + # noinspection PyMethodMayBeStatic def on_api_command(self, command, data): """ Called by OctoPrint upon a POST request to ``/api/plugin/``. ``command`` will contain one of @@ -1164,6 +1211,7 @@ class SimpleApiPlugin(OctoPrintPlugin): """ return None + # noinspection PyMethodMayBeStatic def on_api_get(self, request): """ Called by OctoPrint upon a GET request to ``/api/plugin/``. ``request`` will contain the @@ -1262,6 +1310,7 @@ class BlueprintPlugin(OctoPrintPlugin, RestartNeedingPlugin): return f return decorator + # noinspection PyProtectedMember def get_blueprint(self): """ Creates and returns the blueprint for your plugin. Override this if you want to define and handle your blueprint yourself. @@ -1327,6 +1376,7 @@ class BlueprintPlugin(OctoPrintPlugin, RestartNeedingPlugin): template_folder=template_folder ) + # noinspection PyMethodMayBeStatic def is_blueprint_protected(self): """ Whether a valid API key is needed to access the blueprint (the default) or not. Note that this only restricts @@ -1395,16 +1445,19 @@ class SettingsPlugin(OctoPrintPlugin): should have access to via :meth:`~octoprint.plugin.SettingsPlugin.get_settings_restricted_paths`. OctoPrint will return its settings on the REST API even to anonymous clients, but will filter out fields it know are restricted, therefore you **must** make sure that you specify sensitive information accordingly to limit access as required! - - .. attribute:: _settings - - The :class:`~octoprint.plugin.PluginSettings` instance to use for accessing the plugin's settings. Injected by - the plugin core system upon initialization of the implementation. """ config_version_key = "_config_version" """Key of the field in the settings that holds the configuration format version.""" + # noinspection PyMissingConstructor + def __init__(self): + self._settings = None + """ + The :class:`~octoprint.plugin.PluginSettings` instance to use for accessing the plugin's settings. Injected by + the plugin core system upon initialization of the implementation. + """ + def on_settings_load(self): """ Loads the settings for the plugin, called by the Settings API view in order to retrieve all settings from @@ -1437,6 +1490,7 @@ class SettingsPlugin(OctoPrintPlugin): restricted_paths = self.get_settings_restricted_paths() + # noinspection PyShadowingNames def restrict_path_unless(data, path, condition): if not path: return @@ -1535,6 +1589,7 @@ class SettingsPlugin(OctoPrintPlugin): return diff + # noinspection PyMethodMayBeStatic def get_settings_defaults(self): """ Retrieves the plugin's default settings with which the plugin's settings manager will be initialized. @@ -1544,6 +1599,7 @@ class SettingsPlugin(OctoPrintPlugin): """ return dict() + # noinspection PyMethodMayBeStatic def get_settings_restricted_paths(self): """ Retrieves the list of paths in the plugin's settings which be restricted on the REST API. @@ -1598,6 +1654,7 @@ class SettingsPlugin(OctoPrintPlugin): """ return dict() + # noinspection PyMethodMayBeStatic def get_settings_preprocessors(self): """ Retrieves the plugin's preprocessors to use for preprocessing returned or set values prior to returning/setting @@ -1635,6 +1692,7 @@ class SettingsPlugin(OctoPrintPlugin): """ return dict(), dict() + # noinspection PyMethodMayBeStatic def get_settings_version(self): """ Retrieves the settings format version of the plugin. @@ -1649,6 +1707,7 @@ class SettingsPlugin(OctoPrintPlugin): """ return None + # noinspection PyMethodMayBeStatic def on_settings_migrate(self, target, current): """ Called by OctoPrint if it detects that the installed version of the plugin necessitates a higher settings version @@ -1735,6 +1794,7 @@ class EventHandlerPlugin(OctoPrintPlugin): videos rendering etc. """ + # noinspection PyMethodMayBeStatic def on_event(self, event, payload): """ Called by OctoPrint upon processing of a fired event on the platform. @@ -1753,6 +1813,7 @@ class SlicerPlugin(OctoPrintPlugin): """ + # noinspection PyMethodMayBeStatic def is_slicer_configured(self): """ Unless the return value of this method is ``True``, OctoPrint will not register the slicer within the slicing @@ -1762,6 +1823,7 @@ class SlicerPlugin(OctoPrintPlugin): """ return False + # noinspection PyMethodMayBeStatic def get_slicer_properties(self): """ Plugins should override this method to return a ``dict`` containing a bunch of meta data about the implemented slicer. @@ -1799,6 +1861,7 @@ class SlicerPlugin(OctoPrintPlugin): destination_extensions=["gco", "gcode", "g"] ) + # noinspection PyMethodMayBeStatic def get_slicer_default_profile(self): """ Should return a :class:`~octoprint.slicing.SlicingProfile` containing the default slicing profile to use with @@ -1810,6 +1873,7 @@ class SlicerPlugin(OctoPrintPlugin): """ return None + # noinspection PyMethodMayBeStatic def get_slicer_profile(self, path): """ Should return a :class:`~octoprint.slicing.SlicingProfile` parsed from the slicing profile stored at the @@ -1823,6 +1887,7 @@ class SlicerPlugin(OctoPrintPlugin): """ return None + # noinspection PyMethodMayBeStatic def save_slicer_profile(self, path, profile, allow_overwrite=True, overrides=None): """ Should save the provided :class:`~octoprint.slicing.SlicingProfile` to the indicated ``path``, after applying @@ -1839,6 +1904,7 @@ class SlicerPlugin(OctoPrintPlugin): """ pass + # noinspection PyMethodMayBeStatic def do_slice(self, model_path, printer_profile, machinecode_path=None, profile_path=None, position=None, on_progress=None, on_progress_args=None, on_progress_kwargs=None): """ Called by OctoPrint to slice ``model_path`` for the indicated ``printer_profile``. If the ``machinecode_path`` is ``None``, @@ -1895,6 +1961,7 @@ class SlicerPlugin(OctoPrintPlugin): """ pass + # noinspection PyMethodMayBeStatic def cancel_slicing(self, machinecode_path): """ Cancels the slicing to the indicated file. @@ -1911,16 +1978,18 @@ class ProgressPlugin(OctoPrintPlugin): limited to minimally 1% steps. """ + # noinspection PyMethodMayBeStatic def on_print_progress(self, storage, path, progress): """ Called by OctoPrint on minimally 1% increments during a running print job. - :param string location: Location of the file + :param string storage: Location of the file :param string path: Path of the file :param int progress: Current progress as a value between 0 and 100 """ pass + # noinspection PyMethodMayBeStatic def on_slicing_progress(self, slicer, source_location, source_path, destination_location, destination_path, progress): """ Called by OctoPrint on minimally 1% increments during a running slicing job. @@ -1946,6 +2015,7 @@ class AppPlugin(OctoPrintPlugin): """ + # noinspection PyMethodMayBeStatic def get_additional_apps(self): return [] diff --git a/src/octoprint/plugins/announcements/__init__.py b/src/octoprint/plugins/announcements/__init__.py index e9a83bd1..668bc3cd 100644 --- a/src/octoprint/plugins/announcements/__init__.py +++ b/src/octoprint/plugins/announcements/__init__.py @@ -33,6 +33,7 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.EventHandlerPlugin): + # noinspection PyMissingConstructor def __init__(self): self._cached_channel_configs = None self._cached_channel_configs_mutex = threading.RLock() @@ -184,6 +185,7 @@ class AnnouncementPlugin(octoprint.plugin.AssetPlugin, return hash.hexdigest() + # noinspection PyShadowingNames def condition(lm, etag): return check_etag(etag) diff --git a/src/octoprint/plugins/corewizard/__init__.py b/src/octoprint/plugins/corewizard/__init__.py index 7beb50a8..7cc9fb5f 100644 --- a/src/octoprint/plugins/corewizard/__init__.py +++ b/src/octoprint/plugins/corewizard/__init__.py @@ -1,22 +1,21 @@ # coding=utf-8 from __future__ import absolute_import, division, print_function -__author__ = "Gina Häußge " __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' __copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" - import octoprint.plugin - from flask.ext.babel import gettext +from .subwizards import Subwizards class CoreWizardPlugin(octoprint.plugin.AssetPlugin, octoprint.plugin.TemplatePlugin, octoprint.plugin.WizardPlugin, octoprint.plugin.SettingsPlugin, - octoprint.plugin.BlueprintPlugin): + octoprint.plugin.BlueprintPlugin, + Subwizards): #~~ TemplatePlugin API @@ -34,6 +33,9 @@ class CoreWizardPlugin(octoprint.plugin.AssetPlugin, result = list() for key, method in required.items(): + if not callable(method): + continue + if not method(): continue @@ -92,135 +94,7 @@ class CoreWizardPlugin(octoprint.plugin.AssetPlugin, return result def get_wizard_version(self): - return 2 - - #~~ ACL subwizard - - def _is_acl_wizard_firstrunonly(self): - return True - - def _is_acl_wizard_required(self): - return self._user_manager.enabled and not self._user_manager.hasBeenCustomized() - - def _get_acl_wizard_details(self): - return dict(required=self._is_acl_wizard_required()) - - def _get_acl_wizard_name(self): - return gettext("Access Control") - - def _get_acl_additional_wizard_template_data(self): - return dict(mandatory=self._is_acl_wizard_required()) - - @octoprint.plugin.BlueprintPlugin.route("/acl", methods=["POST"]) - def acl_wizard_api(self): - from flask import request - from octoprint.server.api import valid_boolean_trues, NO_CONTENT - - data = request.values - if hasattr(request, "json") and request.json: - data = request.json - - if "ac" in data and data["ac"] in valid_boolean_trues and \ - "user" in data.keys() and "pass1" in data.keys() and \ - "pass2" in data.keys() and data["pass1"] == data["pass2"]: - # configure access control - self._settings.global_set_boolean(["accessControl", "enabled"], True) - self._user_manager.enable() - self._user_manager.addUser(data["user"], data["pass1"], True, ["user", "admin"], overwrite=True) - elif "ac" in data.keys() and not data["ac"] in valid_boolean_trues: - # disable access control - self._settings.global_set_boolean(["accessControl", "enabled"], False) - - octoprint.server.loginManager.anonymous_user = octoprint.users.DummyUser - octoprint.server.principals.identity_loaders.appendleft(octoprint.users.dummy_identity_loader) - - self._user_manager.disable() - self._settings.save() - return NO_CONTENT - - #~~ Webcam subwizard - - def _is_webcam_wizard_firstrunonly(self): - return True - - def _is_webcam_wizard_required(self): - webcam_snapshot_url = self._settings.global_get(["webcam", "snapshot"]) - webcam_stream_url = self._settings.global_get(["webcam", "stream"]) - ffmpeg_path = self._settings.global_get(["webcam", "ffmpeg"]) - - return not (webcam_snapshot_url and webcam_stream_url and ffmpeg_path) - - def _get_webcam_wizard_details(self): - return dict(required=self._is_webcam_wizard_required()) - - def _get_webcam_wizard_name(self): - return gettext("Webcam & Timelapse") - - #~~ Server commands subwizard - - def _is_servercommands_wizard_firstrunonly(self): - return True - - def _is_servercommands_wizard_required(self): - system_shutdown_command = self._settings.global_get(["server", "commands", "systemShutdownCommand"]) - system_restart_command = self._settings.global_get(["server", "commands", "systemRestartCommand"]) - server_restart_command = self._settings.global_get(["server", "commands", "serverRestartCommand"]) - - return not (system_shutdown_command and system_restart_command and server_restart_command) - - def _get_servercommands_wizard_details(self): - return dict(required=self._is_servercommands_wizard_required()) - - def _get_servercommands_wizard_name(self): - return gettext("Server Commands") - - #~~ Online check subwizard - - def _is_onlinecheck_wizard_firstrunonly(self): - return False - - def _is_onlinecheck_wizard_required(self): - return self._settings.global_get(["server", "onlineCheck", "enabled"]) is None - - def _get_onlinecheck_wizard_details(self): - return dict(required=self._is_onlinecheck_wizard_required()) - - def _get_onlinecheck_wizard_name(self): - return gettext("Online connectivity check") - - def _get_onlinecheck_additional_wizard_template_data(self): - return dict(mandatory=self._is_onlinecheck_wizard_required()) - - #~~ Plugin blacklist subwizard - - def _is_pluginblacklist_wizard_firstrunonly(self): - return False - - def _is_pluginblacklist_wizard_required(self): - return self._settings.global_get(["server", "pluginBlacklist", "enabled"]) is None - - def _get_pluginblacklist_wizard_details(self): - return dict(required=self._is_pluginblacklist_wizard_required()) - - def _get_pluginblacklist_wizard_name(self): - return gettext("Plugin blacklist") - - def _get_pluginblacklist_additional_wizard_template_data(self): - return dict(mandatory=self._is_pluginblacklist_wizard_required()) - - #~~ Printer profile subwizard - - def _is_printerprofile_wizard_firstrunonly(self): - return True - - def _is_printerprofile_wizard_required(self): - return self._printer_profile_manager.is_default_unmodified() and self._printer_profile_manager.profile_count == 1 - - def _get_printerprofile_wizard_details(self): - return dict(required=self._is_printerprofile_wizard_required()) - - def _get_printerprofile_wizard_name(self): - return gettext("Default Printer Profile") + return 3 #~~ helpers diff --git a/src/octoprint/plugins/corewizard/subwizards.py b/src/octoprint/plugins/corewizard/subwizards.py new file mode 100644 index 00000000..a41a4eca --- /dev/null +++ b/src/octoprint/plugins/corewizard/subwizards.py @@ -0,0 +1,154 @@ +# coding=utf-8 +from __future__ import absolute_import, division, print_function + +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' +__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License" + +import octoprint.plugin + +import sys +import inspect +from flask.ext.babel import gettext + + +# noinspection PyUnresolvedReferences,PyMethodMayBeStatic +class ServerCommandsSubwizard(object): + def _is_servercommands_wizard_firstrunonly(self): + return True + + def _is_servercommands_wizard_required(self): + system_shutdown_command = self._settings.global_get(["server", "commands", "systemShutdownCommand"]) + system_restart_command = self._settings.global_get(["server", "commands", "systemRestartCommand"]) + server_restart_command = self._settings.global_get(["server", "commands", "serverRestartCommand"]) + + return not (system_shutdown_command and system_restart_command and server_restart_command) + + def _get_servercommands_wizard_details(self): + return dict(required=self._is_servercommands_wizard_required()) + + def _get_servercommands_wizard_name(self): + return gettext("Server Commands") + + +# noinspection PyUnresolvedReferences,PyMethodMayBeStatic +class WebcamSubwizard(object): + def _is_webcam_wizard_firstrunonly(self): + return True + + def _is_webcam_wizard_required(self): + webcam_snapshot_url = self._settings.global_get(["webcam", "snapshot"]) + webcam_stream_url = self._settings.global_get(["webcam", "stream"]) + ffmpeg_path = self._settings.global_get(["webcam", "ffmpeg"]) + + return not (webcam_snapshot_url and webcam_stream_url and ffmpeg_path) + + def _get_webcam_wizard_details(self): + return dict(required=self._is_webcam_wizard_required()) + + def _get_webcam_wizard_name(self): + return gettext("Webcam & Timelapse") + + +# noinspection PyUnresolvedReferences,PyMethodMayBeStatic +class AclSubwizard(object): + def _is_acl_wizard_firstrunonly(self): + return True + + def _is_acl_wizard_required(self): + return self._user_manager.enabled and not self._user_manager.hasBeenCustomized() + + def _get_acl_wizard_details(self): + return dict(required=self._is_acl_wizard_required()) + + def _get_acl_wizard_name(self): + return gettext("Access Control") + + def _get_acl_additional_wizard_template_data(self): + return dict(mandatory=self._is_acl_wizard_required()) + + @octoprint.plugin.BlueprintPlugin.route("/acl", methods=["POST"]) + def acl_wizard_api(self): + from flask import request, abort + from octoprint.server.api import valid_boolean_trues, NO_CONTENT + + if not self._settings.global_get(["server", "firstRun"]) or self._user_manager.hasBeenCustomized(): + abort(404) + + data = request.values + if hasattr(request, "json") and request.json: + data = request.json + + if "ac" in data and data["ac"] in valid_boolean_trues and \ + "user" in data.keys() and "pass1" in data.keys() and \ + "pass2" in data.keys() and data["pass1"] == data["pass2"]: + # configure access control + self._settings.global_set_boolean(["accessControl", "enabled"], True) + self._user_manager.enable() + self._user_manager.addUser(data["user"], data["pass1"], True, ["user", "admin"], overwrite=True) + elif "ac" in data.keys() and not data["ac"] in valid_boolean_trues: + # disable access control + self._settings.global_set_boolean(["accessControl", "enabled"], False) + + octoprint.server.loginManager.anonymous_user = octoprint.users.DummyUser + octoprint.server.principals.identity_loaders.appendleft(octoprint.users.dummy_identity_loader) + + self._user_manager.disable() + self._settings.save() + return NO_CONTENT + + +# noinspection PyUnresolvedReferences,PyMethodMayBeStatic +class OnlineCheckSubwizard(object): + def _is_onlinecheck_wizard_firstrunonly(self): + return False + + def _is_onlinecheck_wizard_required(self): + return self._settings.global_get(["server", "onlineCheck", "enabled"]) is None + + def _get_onlinecheck_wizard_details(self): + return dict(required=self._is_onlinecheck_wizard_required()) + + def _get_onlinecheck_wizard_name(self): + return gettext("Online connectivity check") + + def _get_onlinecheck_additional_wizard_template_data(self): + return dict(mandatory=self._is_onlinecheck_wizard_required()) + + +# noinspection PyUnresolvedReferences,PyMethodMayBeStatic +class PluginBlacklistSubwizard(object): + def _is_pluginblacklist_wizard_firstrunonly(self): + return False + + def _is_pluginblacklist_wizard_required(self): + return self._settings.global_get(["server", "pluginBlacklist", "enabled"]) is None + + def _get_pluginblacklist_wizard_details(self): + return dict(required=self._is_pluginblacklist_wizard_required()) + + def _get_pluginblacklist_wizard_name(self): + return gettext("Plugin blacklist") + + def _get_pluginblacklist_additional_wizard_template_data(self): + return dict(mandatory=self._is_pluginblacklist_wizard_required()) + + +# noinspection PyUnresolvedReferences,PyMethodMayBeStatic +class PrinterProfileSubwizard(object): + def _is_printerprofile_wizard_firstrunonly(self): + return True + + def _is_printerprofile_wizard_required(self): + return self._printer_profile_manager.is_default_unmodified() and self._printer_profile_manager.profile_count == 1 + + def _get_printerprofile_wizard_details(self): + return dict(required=self._is_printerprofile_wizard_required()) + + def _get_printerprofile_wizard_name(self): + return gettext("Default Printer Profile") + + +Subwizards = type("Subwizwards", + tuple(cls for clsname, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass) + if clsname.endswith("Subwizard")), + dict()) diff --git a/src/octoprint/plugins/cura/__init__.py b/src/octoprint/plugins/cura/__init__.py index 3285316d..732f4f1b 100644 --- a/src/octoprint/plugins/cura/__init__.py +++ b/src/octoprint/plugins/cura/__init__.py @@ -31,6 +31,7 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, octoprint.plugin.StartupPlugin, octoprint.plugin.WizardPlugin): + # noinspection PyMissingConstructor def __init__(self): self._logger = logging.getLogger("octoprint.plugins.cura") self._cura_logger = logging.getLogger("octoprint.plugins.cura.engine") @@ -79,7 +80,7 @@ class CuraPlugin(octoprint.plugin.SlicerPlugin, self._cura_logger.addHandler(cura_logging_handler) self._cura_logger.setLevel(logging.DEBUG if self._settings.get_boolean(["debug_logging"]) else logging.CRITICAL) self._cura_logger.propagate = False - + engine = self._settings.get(["cura_engine"]) if not self._is_engine_configured(cura_engine=engine): self._logger.info("Path to CuraEngine has not been configured or does not exist (currently set to %r), " @@ -519,6 +520,7 @@ def _get_usage_from_length(filament_length, filament_diameter): return usage + __plugin_name__ = "CuraEngine (<= 15.04)" __plugin_author__ = "Gina Häußge" __plugin_url__ = "http://docs.octoprint.org/en/master/bundledplugins/cura.html" diff --git a/src/octoprint/plugins/cura/profile.py b/src/octoprint/plugins/cura/profile.py index c8dd1377..7b74fcdd 100644 --- a/src/octoprint/plugins/cura/profile.py +++ b/src/octoprint/plugins/cura/profile.py @@ -8,7 +8,9 @@ __copyright__ = "Copyright (C) 2014 The OctoPrint Project - Released under terms import re +# noinspection PyCompatibility from builtins import range +# noinspection PyCompatibility from past.builtins import basestring class SupportLocationTypes(object): diff --git a/src/octoprint/plugins/discovery/__init__.py b/src/octoprint/plugins/discovery/__init__.py index 988d4840..46104ac4 100644 --- a/src/octoprint/plugins/discovery/__init__.py +++ b/src/octoprint/plugins/discovery/__init__.py @@ -10,9 +10,10 @@ The SSDP/UPNP implementations has been largely inspired by https://gist.github.c """ import logging -import os import flask from flask.ext.babel import gettext + +# noinspection PyCompatibility from builtins import range import octoprint.plugin @@ -24,14 +25,6 @@ except: pybonjour = False -__plugin_name__ = "Discovery" -__plugin_author__ = "Gina Häußge" -__plugin_url__ = "http://docs.octoprint.org/en/master/bundledplugins/discovery.html" -__plugin_description__ = "Makes the OctoPrint instance discoverable via Bonjour/Avahi/Zeroconf and uPnP" -__plugin_disabling_discouraged__ = gettext("Without this plugin your OctoPrint instance will no longer be " - "discoverable on the network via Bonjour and uPnP.") -__plugin_license__ = "AGPLv3" - def __plugin_load__(): if not pybonjour: # no pybonjour available, we can't use that @@ -62,6 +55,7 @@ class DiscoveryPlugin(octoprint.plugin.StartupPlugin, ssdp_multicast_port = 1900 + # noinspection PyMissingConstructor def __init__(self): self.host = None self.port = None @@ -685,3 +679,12 @@ class DiscoveryPlugin(octoprint.plugin.StartupPlugin, else: import socket return u"OctoPrint instance on {}".format(socket.gethostname()) + + +__plugin_name__ = "Discovery" +__plugin_author__ = "Gina Häußge" +__plugin_url__ = "http://docs.octoprint.org/en/master/bundledplugins/discovery.html" +__plugin_description__ = "Makes the OctoPrint instance discoverable via Bonjour/Avahi/Zeroconf and uPnP" +__plugin_disabling_discouraged__ = gettext("Without this plugin your OctoPrint instance will no longer be " + "discoverable on the network via Bonjour and uPnP.") +__plugin_license__ = "AGPLv3" diff --git a/src/octoprint/plugins/octopi_support/__init__.py b/src/octoprint/plugins/octopi_support/__init__.py index ab1dc79b..19780919 100644 --- a/src/octoprint/plugins/octopi_support/__init__.py +++ b/src/octoprint/plugins/octopi_support/__init__.py @@ -91,6 +91,7 @@ class OctoPiSupportPlugin(octoprint.plugin.EnvironmentDetectionPlugin, octoprint.plugin.AssetPlugin, octoprint.plugin.TemplatePlugin): + # noinspection PyMissingConstructor def __init__(self): self._version = None self._cpuinfo = None diff --git a/src/octoprint/plugins/pluginmanager/__init__.py b/src/octoprint/plugins/pluginmanager/__init__.py index 56f880f1..c8ef95b9 100644 --- a/src/octoprint/plugins/pluginmanager/__init__.py +++ b/src/octoprint/plugins/pluginmanager/__init__.py @@ -12,14 +12,13 @@ import octoprint.plugin.core from octoprint.settings import valid_boolean_trues from octoprint.server.util.flask import restricted_access, with_revalidation_checking, check_etag -from octoprint.server import admin_permission, VERSION -from octoprint.util.pip import LocalPipCaller, UnknownPip +from octoprint.server import admin_permission +from octoprint.util.pip import LocalPipCaller from octoprint.util.version import get_octoprint_version_string, get_octoprint_version, is_octoprint_compatible from octoprint.util.platform import get_os from flask import jsonify, make_response from flask.ext.babel import gettext -from collections import OrderedDict import logging import sarge @@ -27,7 +26,6 @@ import sys import requests import re import os -import pkg_resources import copy import dateutil.parser import time @@ -44,7 +42,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, octoprint.plugin.EventHandlerPlugin): ARCHIVE_EXTENSIONS = (".zip", ".tar.gz", ".tgz", ".tar") - + # valid pip install URL schemes according to https://pip.pypa.io/en/stable/reference/pip_install/ URL_SCHEMES = ("http", "https", "git", "git+http", "git+https", "git+ssh", "git+git", @@ -61,6 +59,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, RECONNECT_HOOKS = ["octoprint.comm.protocol.*",] + # noinspection PyMissingConstructor def __init__(self): self._pending_enable = set() self._pending_disable = set() @@ -313,7 +312,7 @@ class PluginManagerPlugin(octoprint.plugin.SimpleApiPlugin, if url is not None: if not any(map(lambda scheme: url.startswith(scheme + "://"), self.URL_SCHEMES)): raise ValueError("Invalid URL to pip install from") - + source = url source_type = "url" already_installed_check = lambda line: url in line diff --git a/src/octoprint/plugins/softwareupdate/__init__.py b/src/octoprint/plugins/softwareupdate/__init__.py index 630a8d26..adcbb84a 100644 --- a/src/octoprint/plugins/softwareupdate/__init__.py +++ b/src/octoprint/plugins/softwareupdate/__init__.py @@ -15,8 +15,8 @@ import time import logging import logging.handlers import hashlib -import traceback +# noinspection PyCompatibility from concurrent import futures from . import version_checks, updaters, exceptions, util, cli @@ -41,9 +41,10 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, octoprint.plugin.EventHandlerPlugin): COMMIT_TRACKING_TYPES = ("github_commit", "bitbucket_commit") - + DATA_FORMAT_VERSION = "v2" + # noinspection PyMissingConstructor def __init__(self): self._update_in_progress = False self._configured_checks_mutex = threading.Lock() @@ -343,7 +344,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, if "octoprint_type" in data: octoprint_type = data["octoprint_type"] - + if octoprint_type == "github_release": self._settings.set(["checks", "octoprint", "type"], octoprint_type, defaults=defaults, force=True) self._settings.set(["checks", "octoprint", "method"], "pip", defaults=defaults, force=True) @@ -364,7 +365,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, self._settings.set(["checks", "octoprint", "prerelease"], False, defaults=defaults, force=True) self._settings.set(["checks", "octoprint", "prerelease_channel"], None, defaults=defaults, force=True) updated_octoprint_check_config = True - + if updated_octoprint_check_config: self._refresh_configured_checks = True try: @@ -381,11 +382,11 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, if current is None or current < 6: # up until & including config version 5 we didn't set the method parameter for the octoprint check # configuration - + configured_checks = self._settings.get(["checks"], incl_defaults=False) if configured_checks is not None and "octoprint" in configured_checks: octoprint_check = dict(configured_checks["octoprint"]) - + if not "method" in octoprint_check and octoprint_check.get("type") == "git_commit": defaults = dict(plugins=dict(softwareupdate=dict(checks=dict(octoprint=dict(method="pip"))))) self._settings.set(["checks", "octoprint", "method"], "update_script", defaults=defaults) @@ -698,7 +699,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, releaseNotes=release_notes, online=target_online, error=target_error) - + if target == "octoprint" and "released_version" in populated_check: information[target]["released_version"] = populated_check["released_version"] @@ -1023,7 +1024,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, # we compare versions fully, not just the base so that we see a difference # between RCs + stable for the same version release result["force_base"] = False - + if check.get("prerelease", None): # we are tracking prereleases => we want to be on the correct prerelease channel/branch channel = check.get("prerelease_channel", None) @@ -1031,7 +1032,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, # if we have a release channel, we also set our update_branch here to our release channel # in case it's not already set result["update_branch"] = check.get("update_branch", channel) - + else: # we are not tracking prereleases, but aren't on the stable branch either => switch back # to stable branch on update @@ -1049,7 +1050,7 @@ class SoftwareUpdatePlugin(octoprint.plugin.BlueprintPlugin, # branch of the release channel - unequality means we might have to handle # a downgrade result["release_compare"] = "python_unequal" - + elif check.get("pip", None): # we force python unequality check for pip installs, to be able to downgrade result["release_compare"] = "python_unequal"