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.
This commit is contained in:
Gina Häußge 2015-10-30 16:28:34 +01:00
parent 221b497932
commit 8ed3bcb094
3 changed files with 25 additions and 29 deletions

View file

@ -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 <http://lesscss.org/#using-less>`_ on how to do that. If you are developing
your plugin under Windows you might also want to give `WinLESS <http://winless.org/>`_ a look which will run

View file

@ -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

View file

@ -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