Documention for CLI and its development

This commit is contained in:
Gina Häußge 2015-10-29 17:37:05 +01:00
parent d9f0fb1667
commit 3c2d2b579d
7 changed files with 162 additions and 22 deletions

31
docs/modules/cli.rst Normal file
View file

@ -0,0 +1,31 @@
.. _sec-modules-cli:
octoprint.cli
-------------
.. automodule:: octoprint.cli
:members:
.. _sec-modules-cli-devel:
octoprint.cli.devel
-------------------
.. automodule:: octoprint.cli.devel
:members:
.. _sec-modules-cli-plugins:
octoprint.cli.plugins
---------------------
.. automodule:: octoprint.cli.plugins
:members:
.. _sec-modules-cli-server:
octoprint.cli.server
--------------------
.. automodule:: octoprint.cli.server
:members:

View file

@ -7,6 +7,7 @@ Internal Modules
.. toctree::
:maxdepth: 3
cli.rst
filemanager.rst
plugin.rst
printer.rst

View file

@ -46,20 +46,87 @@ octoprint.cli.commands
By providing a handler for this hook plugins may register commands on OctoPrint's command line interface (CLI).
.. todo::
Handlers are expected to return a list of callables annotated as `Click commands <http://click.pocoo.org/5/>`_ to register with the
CLI.
* More documentation
* Example
The custom ``MultiCommand`` instance :class:`~octoprint.cli.plugins.OctoPrintPluginCommands` is provided
as parameter. Via that object handlers may access the *global* :class:`~octoprint.settings.Settings`
and the :class:`~octoprint.plugin.core.PluginManager` instance as ``cli_group.settings`` and ``cli_group.plugin_manager``.
**Example:**
Registers two new commands, ``custom_cli_command:greet`` and ``custom_cli_command:random`` with
OctoPrint:
.. onlineinclude:: https://raw.githubusercontent.com/OctoPrint/Plugin-Examples/master/custom_cli_command.py
:linenos:
:tab-width: 4
:caption: `custom_cli_command.py <https://github.com/OctoPrint/Plugin-Examples/blob/master/custom_cli_command.py>`_
Calling ``octoprint --help`` shows the two new commands:
.. code-block:: none
$ octoprint --help
Usage: octoprint [OPTIONS] COMMAND [ARGS]...
Options:
-b, --basedir PATH Specify the basedir to use for uploads, timelapses etc.
-c, --config PATH Specify the config file to use.
-v, --verbose Increase logging verbosity
--version Show the version and exit.
--help Show this message and exit.
Commands:
custom_cli_command:greet Greet someone by name, the greeting can be...
custom_cli_command:random Greet someone by name with a random greeting.
daemon Starts, stops or restarts in daemon mode.
devel:newplugin Creates a new plugin based on the OctoPrint...
serve Starts the OctoPrint server.
Each also has an individual help output:
.. code-block:: none
$ octoprint custom_cli_command:greet --help
Usage: octoprint custom_cli_command:greet [OPTIONS] [NAME]
Greet someone by name, the greeting can be customized.
Options:
-g, --greeting TEXT The greeting to use
--help Show this message and exit.
$ octoprint custom_cli_command:random --help
Usage: octoprint custom_cli_command:random [OPTIONS] [NAME]
Greet someone by name with a random greeting.
Options:
--help Show this message and exit.
And of course they work too:
.. code-block:: none
$ octoprint custom_cli_command:greet
Hello World!
$ octoprint custom_cli_command:greet --greeting "Good morning"
Good morning World!
$ octoprint custom_cli_command:random stranger
Hola stranger!
.. note::
If your hook handler is an instance method of a plugin mixin implementation, be aware that the hook will be
called without OctoPrint initializing your implementation instance. That means that **none** of the
:ref:`injected properties <sec-plugins-concepts-injectedproperties>` will be available and also the
:meth:`~octoprint.plugin.Plugin.initialize` method will not be called.
:method:`~octoprint.plugin.Plugin.initialize` method will not be called.
Your hook handler will have access to the plugin manager as ``cli_group._plugin_manager`` and to the
*global* settings as ``cli_group._settings``. You can have your handler turn the latter into a
Your hook handler will have access to the plugin manager as ``cli_group.plugin_manager`` and to the
*global* settings as ``cli_group.settings``. You can have your handler turn the latter into a
:class:`~octoprint.plugin.PluginSettings` instance by using :func:`octoprint.plugin.plugin_settings_from_settings_plugin`
if your plugin's implementation implements the :class:`~octoprint.plugin.SettingsPlugin` mixin and inject
that and the plugin manager instance yourself:
@ -92,7 +159,7 @@ octoprint.cli.commands
OctoPrint server context, so there is absolutely no way to access a printer connection, the event bus or
anything else like that. The only things available are the settings and the plugin manager.
:return: A list of `Click commands or groups <http://click.pocoo.org/5/commands/>` to provide on
:return: A list of `Click commands or groups <http://click.pocoo.org/5/commands/>`_ to provide on
OctoPrint's CLI.
:rtype: list

View file

@ -11,16 +11,20 @@ import octoprint
#~~ click context
class OctoPrintContext(object):
def __init__(self, configfile=None, basedir=None, debug=False, verbosity=0):
"""Custom context wrapping the standard options."""
def __init__(self, configfile=None, basedir=None, verbosity=0):
self.configfile = configfile
self.basedir = basedir
self.debug = debug
self.verbosity = verbosity
pass_octoprint_ctx = click.make_pass_decorator(OctoPrintContext, ensure=True)
"""Decorator to pass in the :class:`OctoPrintContext` instance."""
#~~ Custom click option to hide from help
class HiddenOption(click.Option):
"""Custom option sub class with empty help."""
def get_help_record(self, ctx):
pass
@ -55,6 +59,13 @@ def set_ctx_obj_option(ctx, param, value):
#~~ helper for setting a lot of bulk options
def bulk_options(options):
"""
Utility decorator to decorate a function with a list of click decorators.
The provided list of ``options`` will be reversed to ensure correct
processing order (inverse from what would be intuitive).
"""
def decorator(f):
options.reverse()
for option in options:
@ -65,6 +76,14 @@ def bulk_options(options):
#~~ helper for setting --basedir, --config and --verbose options
def standard_options(hidden=False):
"""
Decorator to add the standard options shared among all "octoprint" commands.
Adds the options ``--basedir``, ``--config`` and ``--verbose``. If ``hidden``
is set to ``True``, the options will be available on the command but not
listed in its help page.
"""
factory = click.option
if hidden:
factory = hidden_option
@ -91,6 +110,9 @@ legacy_options = bulk_options([
hidden_option("--pid", type=click.Path(), default="/tmp/octoprint.pid"),
hidden_option("--iknowwhatimdoing", "allow_root", is_flag=True),
])
"""Legacy options available directly on the "octoprint" command in earlier versions.
Kept available for reasons of backwards compatibility, but hidden from the
generated help pages."""
#~~ "octoprint" command, merges server_commands and plugin_commands groups

View file

@ -9,8 +9,12 @@ __copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms
import click
import logging
class OctoPrintDevelCommands(click.MultiCommand):
"""
Custom `click.MultiCommand <http://click.pocoo.org/5/api/#click.MultiCommand>`_
implementation that provides commands relevant for (plugin) development
based on availability of development dependencies.
"""
sep = ":"

View file

@ -14,21 +14,32 @@ from octoprint.cli import pass_octoprint_ctx, OctoPrintContext
class OctoPrintPluginCommands(click.MultiCommand):
"""
Custom `click.MultiCommand` implementation that collects commands from
the plugin hook "octoprint.cli.commands".
Custom `click.MultiCommand <http://click.pocoo.org/5/api/#click.MultiCommand>`_
implementation that collects commands from the plugin hook
:ref:`octoprint.cli.commands <sec-plugins-hook-cli-commands>`.
.. attribute:: settings
The global :class:`~octoprint.settings.Settings` instance.
.. attribute:: plugin_manager
The :class:`~octoprint.plugin.core.PluginManager` instance.
"""
sep = ":"
"""Separator for commands between plugin name and command name."""
def __init__(self, *args, **kwargs):
click.MultiCommand.__init__(self, *args, **kwargs)
self._settings = None
self._plugin_manager = None
self.settings = None
self.plugin_manager = None
self._logger = logging.getLogger(__name__)
self._initialized = False
def _initialize(self, ctx):
if self._settings is not None:
if self._initialized:
return
if ctx.obj is None:
@ -37,11 +48,13 @@ class OctoPrintPluginCommands(click.MultiCommand):
# initialize settings and plugin manager based on provided
# context (basedir and configfile)
from octoprint import init_settings, init_pluginsystem
self._settings = init_settings(ctx.obj.basedir, ctx.obj.configfile)
self._plugin_manager = init_pluginsystem(self._settings)
self.settings = init_settings(ctx.obj.basedir, ctx.obj.configfile)
self.plugin_manager = init_pluginsystem(self.settings)
# fetch registered hooks
self._hooks = self._plugin_manager.get_hooks("octoprint.cli.commands")
self.hooks = self.plugin_manager.get_hooks("octoprint.cli.commands")
self._initialized = True
def list_commands(self, ctx):
self._initialize(ctx)
@ -60,7 +73,7 @@ class OctoPrintPluginCommands(click.MultiCommand):
import collections
result = collections.OrderedDict()
for name, hook in self._hooks.items():
for name, hook in self.hooks.items():
try:
commands = hook(self, pass_octoprint_ctx)
for command in commands:

View file

@ -12,6 +12,8 @@ import sys
from octoprint.cli import pass_octoprint_ctx, bulk_options, standard_options
def run_server(basedir, configfile, host, port, debug, allow_root, logging_config, verbosity):
"""Initializes the environment and starts up the server."""
from octoprint import init_platform, __display_version__
def log_startup(_):
@ -39,10 +41,8 @@ def run_server(basedir, configfile, host, port, debug, allow_root, logging_confi
octoprint_server = Server(settings=settings, plugin_manager=plugin_manager, host=host, port=port, debug=debug, allow_root=allow_root)
octoprint_server.run()
#~~ server options
server_options = bulk_options([
click.option("--host", type=click.STRING,
help="Specify the host on which to bind the server."),
@ -54,6 +54,8 @@ server_options = bulk_options([
help="Allow OctoPrint to run as user root."),
click.option("--debug", is_flag=True, help="Enable debug mode"),
])
"""Decorator to add the options shared among the server commands: ``--host``, ``--port``,
``--logging``, ``--iknowwhatimdoing`` and ``--debug``."""
#~~ "octoprint serve" and "octoprint daemon" commands