From 8ed3bcb094e418659f8e1a5d9ba82a0096adf5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Fri, 30 Oct 2015 16:28:34 +0100 Subject: [PATCH] Slight changes in CLI structure for performance reasons Having the plugin commands on the first level of --help proved to be a bad idea since it basically kills every chance of lazy loading the (expensive) plugin context. Using a sub command for anything plugin related allows us to only fire up the plugin context if a plugin command is expected, saving us some precious seconds of operation in all other cases. For conformity reasons the dev sub commands were now restructured similarly. --- docs/plugins/gettingstarted.rst | 14 +++++++------- src/octoprint/cli/dev.py | 25 ++++++++++++------------- src/octoprint/cli/plugins.py | 15 ++++++--------- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/docs/plugins/gettingstarted.rst b/docs/plugins/gettingstarted.rst index 7ef79fa1..63c7ece7 100644 --- a/docs/plugins/gettingstarted.rst +++ b/docs/plugins/gettingstarted.rst @@ -135,10 +135,10 @@ so we'll first need to install that:: (venv) $ pip install cookiecutter -Then we can use the ``octoprint dev:plugin new`` command [#f1]_ to generate a new OctoPrint plugin skeleton for us:: +Then we can use the ``octoprint dev plugin:new`` command [#f1]_ to generate a new OctoPrint plugin skeleton for us:: (venv) $ cd ~/devel - (venv) $ octoprint dev:plugin new helloworld + (venv) $ octoprint dev plugin:new helloworld Cloning into 'cookiecutter-octoprint-plugin'... remote: Counting objects: 101, done. remote: Total 101 (delta 0), reused 0 (delta 0), pack-reused 101 @@ -160,7 +160,7 @@ Then we can use the ``octoprint dev:plugin new`` command [#f1]_ to generate a ne .. note:: - If ``octoprint dev:plugin new`` isn't recognized as a command (and also doesn't show up in the output of + If ``octoprint dev plugin:new`` isn't recognized as a command (and also doesn't show up in the output of ``octoprint --help``, make sure you installed cookiecutter into the same python environment as OctoPrint. This will create a project structure in the ``OctoPrint-HelloWorld`` folder we just changed to that looks like this:: @@ -230,10 +230,10 @@ Now all that's left to do is to move our ``helloworld.py`` into the ``octoprint_ The plugin is now ready to be installed via ``python setup.py install``. However, since we are still working on our plugin, it makes more sense to use ``python setup.py develop`` for now -- this way the plugin becomes discoverable by OctoPrint, however we don't have to reinstall it after any changes we will still do. We can have the -``octoprint dev:plugin install`` command do everything for us here, it will ensure to use the python binary belonging +``octoprint dev plugin:install`` command do everything for us here, it will ensure to use the python binary belonging to your OctoPrint installation:: - (venv) $ octoprint dev:plugin install + (venv) $ octoprint dev plugin:install running develop running egg_info creating OctoPrint_HelloWorld.egg-info @@ -1075,11 +1075,11 @@ looking for examples. .. rubric:: Footnotes -.. [#f1] Instead of the ``octoprint dev:plugin new`` you could also have manually called cookiecutter with the +.. [#f1] Instead of the ``octoprint dev plugin:new`` you could also have manually called cookiecutter with the template's repository URL shortcut: ``cookiecutter gh:OctoPrint/cookiecutter-octoprint-plugin``. The ``devel:newplugin`` command already does this for you, makes sure cookiecutter always uses a fresh checkout without prompting you for it and also allows to pre-specify a bunch of settings (like the - plugin's identifier) directly from the command line. Take a look at ``octoprint dev:plugin new --help`` + plugin's identifier) directly from the command line. Take a look at ``octoprint dev plugin:new --help`` for the usage details. .. [#f2] Refer to the `LESS documentation `_ on how to do that. If you are developing your plugin under Windows you might also want to give `WinLESS `_ a look which will run diff --git a/src/octoprint/cli/dev.py b/src/octoprint/cli/dev.py index 4e712d2f..d9902e8f 100644 --- a/src/octoprint/cli/dev.py +++ b/src/octoprint/cli/dev.py @@ -15,8 +15,8 @@ class OctoPrintDevelCommands(click.MultiCommand): based on availability of development dependencies. """ - prefix = "dev" sep = ":" + groups = ("plugin",) def __init__(self, *args, **kwargs): click.MultiCommand.__init__(self, *args, **kwargs) @@ -47,10 +47,11 @@ class OctoPrintDevelCommands(click.MultiCommand): yield result def _get_commands(self): - commands = dict() - for command in self._get_commands_from_prefix_methods("group_"): - commands[self.prefix + self.sep + command.name] = command - return commands + result = dict() + for group in self.groups: + for command in self._get_commands_from_prefix_methods("{}_".format(group)): + result[group + self.sep + command.name] = command + return result def list_commands(self, ctx): result = [name for name in self._get_commands()] @@ -61,13 +62,6 @@ class OctoPrintDevelCommands(click.MultiCommand): commands = self._get_commands() return commands.get(cmd_name, None) - def group_plugin(self): - command_group = click.Group("plugin", - help="Helpers for plugin developers") - for command in self._get_commands_from_prefix_methods("plugin_"): - command_group.add_command(command) - return command_group - def plugin_new(self): try: import cookiecutter.main @@ -229,6 +223,11 @@ class OctoPrintDevelCommands(click.MultiCommand): return command -@click.group(cls=OctoPrintDevelCommands) +@click.group() def dev_commands(): pass + +@dev_commands.group(name="dev", cls=OctoPrintDevelCommands) +def dev(): + """Additional commands for development tasks.""" + pass diff --git a/src/octoprint/cli/plugins.py b/src/octoprint/cli/plugins.py index 5b9f3623..47bf887a 100644 --- a/src/octoprint/cli/plugins.py +++ b/src/octoprint/cli/plugins.py @@ -27,7 +27,6 @@ class OctoPrintPluginCommands(click.MultiCommand): The :class:`~octoprint.plugin.core.PluginManager` instance. """ - prefix = "plugin" sep = ":" def __init__(self, *args, **kwargs): @@ -77,26 +76,24 @@ class OctoPrintPluginCommands(click.MultiCommand): for name, hook in self.hooks.items(): try: - plugin_info = self.plugin_manager.get_plugin_info(name, require_enabled=False) - command_group = click.Group(name=name, help="{} commands".format(plugin_info.name)) - commands = hook(self, pass_octoprint_ctx) for command in commands: if not isinstance(command, click.Command): self._logger.warn("Plugin {} provided invalid CLI command, ignoring it: {!r}".format(name, command)) continue - command_group.add_command(command) - - result[self.prefix + self.sep + name] = command_group + result[name + self.sep + command.name] = command except: self._logger.exception("Error while retrieving cli commants for plugin {}".format(name)) return result -@click.group(cls=OctoPrintPluginCommands) +@click.group() @pass_octoprint_ctx def plugin_commands(obj): - """Commands provided by plugins.""" logging.basicConfig(level=logging.DEBUG if obj.verbosity > 0 else logging.WARN) +@plugin_commands.group(name="plugins", cls=OctoPrintPluginCommands) +def plugins(): + """Additional commands provided by plugins.""" + pass