That should solve any weird import issues we have when running gcodeInterpreter.py directly (and hence putting octoprint.util as first entry into the python path, causing potential issues with imported modules such as yaml to catch the octoprint.util.platform module instead of the actual python platform module). See reported problem with that by @CapnBry in #2095
174 lines
6.2 KiB
Python
174 lines
6.2 KiB
Python
# 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 click
|
|
import octoprint
|
|
import sys
|
|
|
|
#~~ click context
|
|
|
|
class OctoPrintContext(object):
|
|
"""Custom context wrapping the standard options."""
|
|
|
|
def __init__(self, configfile=None, basedir=None, verbosity=0, safe_mode=False):
|
|
self.configfile = configfile
|
|
self.basedir = basedir
|
|
self.verbosity = verbosity
|
|
self.safe_mode = safe_mode
|
|
|
|
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
|
|
|
|
def hidden_option(*param_decls, **attrs):
|
|
"""Attaches a hidden option to the command. All positional arguments are
|
|
passed as parameter declarations to :class:`Option`; all keyword
|
|
arguments are forwarded unchanged. This is equivalent to creating an
|
|
:class:`Option` instance manually and attaching it to the
|
|
:attr:`Command.params` list.
|
|
"""
|
|
|
|
import inspect
|
|
from click.decorators import _param_memo
|
|
|
|
def decorator(f):
|
|
if 'help' in attrs:
|
|
attrs['help'] = inspect.cleandoc(attrs['help'])
|
|
_param_memo(f, HiddenOption(param_decls, **attrs))
|
|
return f
|
|
return decorator
|
|
|
|
#~~ helper for setting context options
|
|
|
|
def set_ctx_obj_option(ctx, param, value):
|
|
"""Helper for setting eager options on the context."""
|
|
if ctx.obj is None:
|
|
ctx.obj = OctoPrintContext()
|
|
if value != param.default:
|
|
setattr(ctx.obj, param.name, value)
|
|
elif param.default is not None:
|
|
setattr(ctx.obj, param.name, param.default)
|
|
|
|
#~~ helper for retrieving context options
|
|
|
|
def get_ctx_obj_option(ctx, key, default, include_parents=True):
|
|
if include_parents and hasattr(ctx, "parent") and ctx.parent:
|
|
fallback = get_ctx_obj_option(ctx.parent, key, default)
|
|
else:
|
|
fallback = default
|
|
return getattr(ctx.obj, key, fallback)
|
|
|
|
#~~ 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:
|
|
option(f)
|
|
return f
|
|
return decorator
|
|
|
|
#~~ 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
|
|
|
|
options = [
|
|
factory("--basedir", "-b", type=click.Path(), callback=set_ctx_obj_option, is_eager=True, expose_value=False,
|
|
help="Specify the basedir to use for uploads, timelapses etc."),
|
|
factory("--config", "-c", "configfile", type=click.Path(), callback=set_ctx_obj_option, is_eager=True, expose_value=False,
|
|
help="Specify the config file to use."),
|
|
factory("--verbose", "-v", "verbosity", count=True, callback=set_ctx_obj_option, is_eager=True, expose_value=False,
|
|
help="Increase logging verbosity"),
|
|
factory("--safe", "safe_mode", is_flag=True, callback=set_ctx_obj_option, is_eager=True, expose_value=False,
|
|
help="Enable safe mode; disables all third party plugins")
|
|
]
|
|
|
|
return bulk_options(options)
|
|
|
|
#~~ helper for settings legacy options we still have to support on "octoprint"
|
|
|
|
legacy_options = bulk_options([
|
|
hidden_option("--host", type=click.STRING, callback=set_ctx_obj_option),
|
|
hidden_option("--port", type=click.INT, callback=set_ctx_obj_option),
|
|
hidden_option("--logging", type=click.Path(), callback=set_ctx_obj_option),
|
|
hidden_option("--debug", "-d", is_flag=True, callback=set_ctx_obj_option),
|
|
hidden_option("--daemon", type=click.Choice(["start", "stop", "restart"]), callback=set_ctx_obj_option),
|
|
hidden_option("--pid", type=click.Path(), default="/tmp/octoprint.pid", callback=set_ctx_obj_option),
|
|
hidden_option("--iknowwhatimdoing", "allow_root", is_flag=True, callback=set_ctx_obj_option),
|
|
])
|
|
"""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
|
|
|
|
from .server import server_commands
|
|
from .plugins import plugin_commands
|
|
from .dev import dev_commands
|
|
from .client import client_commands
|
|
from .config import config_commands
|
|
from .analysis import analysis_commands
|
|
|
|
@click.group(name="octoprint", invoke_without_command=True, cls=click.CommandCollection,
|
|
sources=[server_commands, plugin_commands, dev_commands, client_commands, config_commands, analysis_commands])
|
|
@standard_options()
|
|
@legacy_options
|
|
@click.version_option(version=octoprint.__version__, allow_from_autoenv=False)
|
|
@click.pass_context
|
|
def octo(ctx, **kwargs):
|
|
|
|
if ctx.invoked_subcommand is None:
|
|
# We have to support calling the octoprint command without any
|
|
# sub commands to remain backwards compatible.
|
|
#
|
|
# But better print a message to inform people that they should
|
|
# use the sub commands instead.
|
|
|
|
def get_value(key):
|
|
return get_ctx_obj_option(ctx, key, kwargs.get(key))
|
|
daemon = get_value("daemon")
|
|
|
|
if daemon:
|
|
click.echo("Daemon operation via \"octoprint --daemon "
|
|
"start|stop|restart\" is deprecated, please use "
|
|
"\"octoprint daemon start|stop|restart\" from now on")
|
|
|
|
if sys.platform == "win32" or sys.platform == "darwin":
|
|
click.echo("Sorry, daemon mode is not supported under your operating system right now")
|
|
else:
|
|
from octoprint.cli.server import daemon_command
|
|
ctx.invoke(daemon_command, command=daemon, **kwargs)
|
|
else:
|
|
click.echo("Starting the server via \"octoprint\" is deprecated, "
|
|
"please use \"octoprint serve\" from now on.")
|
|
|
|
from octoprint.cli.server import serve_command
|
|
ctx.invoke(serve_command, **kwargs)
|