mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-18 22:20:30 +00:00
1739 lines
68 KiB
Python
1739 lines
68 KiB
Python
|
import os
|
||
|
import sys
|
||
|
from contextlib import contextmanager
|
||
|
from itertools import repeat
|
||
|
from functools import update_wrapper
|
||
|
|
||
|
from .types import convert_type, IntRange, BOOL
|
||
|
from .utils import make_str, make_default_short_help, echo, get_os_args
|
||
|
from .exceptions import ClickException, UsageError, BadParameter, Abort, \
|
||
|
MissingParameter
|
||
|
from .termui import prompt, confirm
|
||
|
from .formatting import HelpFormatter, join_options
|
||
|
from .parser import OptionParser, split_opt
|
||
|
from .globals import push_context, pop_context
|
||
|
|
||
|
from ._compat import PY2, isidentifier, iteritems
|
||
|
from ._unicodefun import _check_for_unicode_literals, _verify_python3_env
|
||
|
|
||
|
|
||
|
_missing = object()
|
||
|
|
||
|
|
||
|
SUBCOMMAND_METAVAR = 'COMMAND [ARGS]...'
|
||
|
SUBCOMMANDS_METAVAR = 'COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...'
|
||
|
|
||
|
|
||
|
def _bashcomplete(cmd, prog_name, complete_var=None):
|
||
|
"""Internal handler for the bash completion support."""
|
||
|
if complete_var is None:
|
||
|
complete_var = '_%s_COMPLETE' % (prog_name.replace('-', '_')).upper()
|
||
|
complete_instr = os.environ.get(complete_var)
|
||
|
if not complete_instr:
|
||
|
return
|
||
|
|
||
|
from ._bashcomplete import bashcomplete
|
||
|
if bashcomplete(cmd, prog_name, complete_var, complete_instr):
|
||
|
sys.exit(1)
|
||
|
|
||
|
|
||
|
def _check_multicommand(base_command, cmd_name, cmd, register=False):
|
||
|
if not base_command.chain or not isinstance(cmd, MultiCommand):
|
||
|
return
|
||
|
if register:
|
||
|
hint = 'It is not possible to add multi commands as children to ' \
|
||
|
'another multi command that is in chain mode'
|
||
|
else:
|
||
|
hint = 'Found a multi command as subcommand to a multi command ' \
|
||
|
'that is in chain mode. This is not supported'
|
||
|
raise RuntimeError('%s. Command "%s" is set to chain and "%s" was '
|
||
|
'added as subcommand but it in itself is a '
|
||
|
'multi command. ("%s" is a %s within a chained '
|
||
|
'%s named "%s"). This restriction was supposed to '
|
||
|
'be lifted in 6.0 but the fix was flawed. This '
|
||
|
'will be fixed in Click 7.0' % (
|
||
|
hint, base_command.name, cmd_name,
|
||
|
cmd_name, cmd.__class__.__name__,
|
||
|
base_command.__class__.__name__,
|
||
|
base_command.name))
|
||
|
|
||
|
|
||
|
def batch(iterable, batch_size):
|
||
|
return list(zip(*repeat(iter(iterable), batch_size)))
|
||
|
|
||
|
|
||
|
def invoke_param_callback(callback, ctx, param, value):
|
||
|
code = getattr(callback, '__code__', None)
|
||
|
args = getattr(code, 'co_argcount', 3)
|
||
|
|
||
|
if args < 3:
|
||
|
# This will become a warning in Click 3.0:
|
||
|
from warnings import warn
|
||
|
warn(Warning('Invoked legacy parameter callback "%s". The new '
|
||
|
'signature for such callbacks starting with '
|
||
|
'click 2.0 is (ctx, param, value).'
|
||
|
% callback), stacklevel=3)
|
||
|
return callback(ctx, value)
|
||
|
return callback(ctx, param, value)
|
||
|
|
||
|
|
||
|
@contextmanager
|
||
|
def augment_usage_errors(ctx, param=None):
|
||
|
"""Context manager that attaches extra information to exceptions that
|
||
|
fly.
|
||
|
"""
|
||
|
try:
|
||
|
yield
|
||
|
except BadParameter as e:
|
||
|
if e.ctx is None:
|
||
|
e.ctx = ctx
|
||
|
if param is not None and e.param is None:
|
||
|
e.param = param
|
||
|
raise
|
||
|
except UsageError as e:
|
||
|
if e.ctx is None:
|
||
|
e.ctx = ctx
|
||
|
raise
|
||
|
|
||
|
|
||
|
def iter_params_for_processing(invocation_order, declaration_order):
|
||
|
"""Given a sequence of parameters in the order as should be considered
|
||
|
for processing and an iterable of parameters that exist, this returns
|
||
|
a list in the correct order as they should be processed.
|
||
|
"""
|
||
|
def sort_key(item):
|
||
|
try:
|
||
|
idx = invocation_order.index(item)
|
||
|
except ValueError:
|
||
|
idx = float('inf')
|
||
|
return (not item.is_eager, idx)
|
||
|
|
||
|
return sorted(declaration_order, key=sort_key)
|
||
|
|
||
|
|
||
|
class Context(object):
|
||
|
"""The context is a special internal object that holds state relevant
|
||
|
for the script execution at every single level. It's normally invisible
|
||
|
to commands unless they opt-in to getting access to it.
|
||
|
|
||
|
The context is useful as it can pass internal objects around and can
|
||
|
control special execution features such as reading data from
|
||
|
environment variables.
|
||
|
|
||
|
A context can be used as context manager in which case it will call
|
||
|
:meth:`close` on teardown.
|
||
|
|
||
|
.. versionadded:: 2.0
|
||
|
Added the `resilient_parsing`, `help_option_names`,
|
||
|
`token_normalize_func` parameters.
|
||
|
|
||
|
.. versionadded:: 3.0
|
||
|
Added the `allow_extra_args` and `allow_interspersed_args`
|
||
|
parameters.
|
||
|
|
||
|
.. versionadded:: 4.0
|
||
|
Added the `color`, `ignore_unknown_options`, and
|
||
|
`max_content_width` parameters.
|
||
|
|
||
|
:param command: the command class for this context.
|
||
|
:param parent: the parent context.
|
||
|
:param info_name: the info name for this invocation. Generally this
|
||
|
is the most descriptive name for the script or
|
||
|
command. For the toplevel script it is usually
|
||
|
the name of the script, for commands below it it's
|
||
|
the name of the script.
|
||
|
:param obj: an arbitrary object of user data.
|
||
|
:param auto_envvar_prefix: the prefix to use for automatic environment
|
||
|
variables. If this is `None` then reading
|
||
|
from environment variables is disabled. This
|
||
|
does not affect manually set environment
|
||
|
variables which are always read.
|
||
|
:param default_map: a dictionary (like object) with default values
|
||
|
for parameters.
|
||
|
:param terminal_width: the width of the terminal. The default is
|
||
|
inherit from parent context. If no context
|
||
|
defines the terminal width then auto
|
||
|
detection will be applied.
|
||
|
:param max_content_width: the maximum width for content rendered by
|
||
|
Click (this currently only affects help
|
||
|
pages). This defaults to 80 characters if
|
||
|
not overridden. In other words: even if the
|
||
|
terminal is larger than that, Click will not
|
||
|
format things wider than 80 characters by
|
||
|
default. In addition to that, formatters might
|
||
|
add some safety mapping on the right.
|
||
|
:param resilient_parsing: if this flag is enabled then Click will
|
||
|
parse without any interactivity or callback
|
||
|
invocation. This is useful for implementing
|
||
|
things such as completion support.
|
||
|
:param allow_extra_args: if this is set to `True` then extra arguments
|
||
|
at the end will not raise an error and will be
|
||
|
kept on the context. The default is to inherit
|
||
|
from the command.
|
||
|
:param allow_interspersed_args: if this is set to `False` then options
|
||
|
and arguments cannot be mixed. The
|
||
|
default is to inherit from the command.
|
||
|
:param ignore_unknown_options: instructs click to ignore options it does
|
||
|
not know and keeps them for later
|
||
|
processing.
|
||
|
:param help_option_names: optionally a list of strings that define how
|
||
|
the default help parameter is named. The
|
||
|
default is ``['--help']``.
|
||
|
:param token_normalize_func: an optional function that is used to
|
||
|
normalize tokens (options, choices,
|
||
|
etc.). This for instance can be used to
|
||
|
implement case insensitive behavior.
|
||
|
:param color: controls if the terminal supports ANSI colors or not. The
|
||
|
default is autodetection. This is only needed if ANSI
|
||
|
codes are used in texts that Click prints which is by
|
||
|
default not the case. This for instance would affect
|
||
|
help output.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, command, parent=None, info_name=None, obj=None,
|
||
|
auto_envvar_prefix=None, default_map=None,
|
||
|
terminal_width=None, max_content_width=None,
|
||
|
resilient_parsing=False, allow_extra_args=None,
|
||
|
allow_interspersed_args=None,
|
||
|
ignore_unknown_options=None, help_option_names=None,
|
||
|
token_normalize_func=None, color=None):
|
||
|
#: the parent context or `None` if none exists.
|
||
|
self.parent = parent
|
||
|
#: the :class:`Command` for this context.
|
||
|
self.command = command
|
||
|
#: the descriptive information name
|
||
|
self.info_name = info_name
|
||
|
#: the parsed parameters except if the value is hidden in which
|
||
|
#: case it's not remembered.
|
||
|
self.params = {}
|
||
|
#: the leftover arguments.
|
||
|
self.args = []
|
||
|
#: protected arguments. These are arguments that are prepended
|
||
|
#: to `args` when certain parsing scenarios are encountered but
|
||
|
#: must be never propagated to another arguments. This is used
|
||
|
#: to implement nested parsing.
|
||
|
self.protected_args = []
|
||
|
if obj is None and parent is not None:
|
||
|
obj = parent.obj
|
||
|
#: the user object stored.
|
||
|
self.obj = obj
|
||
|
self._meta = getattr(parent, 'meta', {})
|
||
|
|
||
|
#: A dictionary (-like object) with defaults for parameters.
|
||
|
if default_map is None \
|
||
|
and parent is not None \
|
||
|
and parent.default_map is not None:
|
||
|
default_map = parent.default_map.get(info_name)
|
||
|
self.default_map = default_map
|
||
|
|
||
|
#: This flag indicates if a subcommand is going to be executed. A
|
||
|
#: group callback can use this information to figure out if it's
|
||
|
#: being executed directly or because the execution flow passes
|
||
|
#: onwards to a subcommand. By default it's None, but it can be
|
||
|
#: the name of the subcommand to execute.
|
||
|
#:
|
||
|
#: If chaining is enabled this will be set to ``'*'`` in case
|
||
|
#: any commands are executed. It is however not possible to
|
||
|
#: figure out which ones. If you require this knowledge you
|
||
|
#: should use a :func:`resultcallback`.
|
||
|
self.invoked_subcommand = None
|
||
|
|
||
|
if terminal_width is None and parent is not None:
|
||
|
terminal_width = parent.terminal_width
|
||
|
#: The width of the terminal (None is autodetection).
|
||
|
self.terminal_width = terminal_width
|
||
|
|
||
|
if max_content_width is None and parent is not None:
|
||
|
max_content_width = parent.max_content_width
|
||
|
#: The maximum width of formatted content (None implies a sensible
|
||
|
#: default which is 80 for most things).
|
||
|
self.max_content_width = max_content_width
|
||
|
|
||
|
if allow_extra_args is None:
|
||
|
allow_extra_args = command.allow_extra_args
|
||
|
#: Indicates if the context allows extra args or if it should
|
||
|
#: fail on parsing.
|
||
|
#:
|
||
|
#: .. versionadded:: 3.0
|
||
|
self.allow_extra_args = allow_extra_args
|
||
|
|
||
|
if allow_interspersed_args is None:
|
||
|
allow_interspersed_args = command.allow_interspersed_args
|
||
|
#: Indicates if the context allows mixing of arguments and
|
||
|
#: options or not.
|
||
|
#:
|
||
|
#: .. versionadded:: 3.0
|
||
|
self.allow_interspersed_args = allow_interspersed_args
|
||
|
|
||
|
if ignore_unknown_options is None:
|
||
|
ignore_unknown_options = command.ignore_unknown_options
|
||
|
#: Instructs click to ignore options that a command does not
|
||
|
#: understand and will store it on the context for later
|
||
|
#: processing. This is primarily useful for situations where you
|
||
|
#: want to call into external programs. Generally this pattern is
|
||
|
#: strongly discouraged because it's not possibly to losslessly
|
||
|
#: forward all arguments.
|
||
|
#:
|
||
|
#: .. versionadded:: 4.0
|
||
|
self.ignore_unknown_options = ignore_unknown_options
|
||
|
|
||
|
if help_option_names is None:
|
||
|
if parent is not None:
|
||
|
help_option_names = parent.help_option_names
|
||
|
else:
|
||
|
help_option_names = ['--help']
|
||
|
|
||
|
#: The names for the help options.
|
||
|
self.help_option_names = help_option_names
|
||
|
|
||
|
if token_normalize_func is None and parent is not None:
|
||
|
token_normalize_func = parent.token_normalize_func
|
||
|
|
||
|
#: An optional normalization function for tokens. This is
|
||
|
#: options, choices, commands etc.
|
||
|
self.token_normalize_func = token_normalize_func
|
||
|
|
||
|
#: Indicates if resilient parsing is enabled. In that case Click
|
||
|
#: will do its best to not cause any failures.
|
||
|
self.resilient_parsing = resilient_parsing
|
||
|
|
||
|
# If there is no envvar prefix yet, but the parent has one and
|
||
|
# the command on this level has a name, we can expand the envvar
|
||
|
# prefix automatically.
|
||
|
if auto_envvar_prefix is None:
|
||
|
if parent is not None \
|
||
|
and parent.auto_envvar_prefix is not None and \
|
||
|
self.info_name is not None:
|
||
|
auto_envvar_prefix = '%s_%s' % (parent.auto_envvar_prefix,
|
||
|
self.info_name.upper())
|
||
|
else:
|
||
|
self.auto_envvar_prefix = auto_envvar_prefix.upper()
|
||
|
self.auto_envvar_prefix = auto_envvar_prefix
|
||
|
|
||
|
if color is None and parent is not None:
|
||
|
color = parent.color
|
||
|
|
||
|
#: Controls if styling output is wanted or not.
|
||
|
self.color = color
|
||
|
|
||
|
self._close_callbacks = []
|
||
|
self._depth = 0
|
||
|
|
||
|
def __enter__(self):
|
||
|
self._depth += 1
|
||
|
push_context(self)
|
||
|
return self
|
||
|
|
||
|
def __exit__(self, exc_type, exc_value, tb):
|
||
|
self._depth -= 1
|
||
|
if self._depth == 0:
|
||
|
self.close()
|
||
|
pop_context()
|
||
|
|
||
|
@contextmanager
|
||
|
def scope(self, cleanup=True):
|
||
|
"""This helper method can be used with the context object to promote
|
||
|
it to the current thread local (see :func:`get_current_context`).
|
||
|
The default behavior of this is to invoke the cleanup functions which
|
||
|
can be disabled by setting `cleanup` to `False`. The cleanup
|
||
|
functions are typically used for things such as closing file handles.
|
||
|
|
||
|
If the cleanup is intended the context object can also be directly
|
||
|
used as a context manager.
|
||
|
|
||
|
Example usage::
|
||
|
|
||
|
with ctx.scope():
|
||
|
assert get_current_context() is ctx
|
||
|
|
||
|
This is equivalent::
|
||
|
|
||
|
with ctx:
|
||
|
assert get_current_context() is ctx
|
||
|
|
||
|
.. versionadded:: 5.0
|
||
|
|
||
|
:param cleanup: controls if the cleanup functions should be run or
|
||
|
not. The default is to run these functions. In
|
||
|
some situations the context only wants to be
|
||
|
temporarily pushed in which case this can be disabled.
|
||
|
Nested pushes automatically defer the cleanup.
|
||
|
"""
|
||
|
if not cleanup:
|
||
|
self._depth += 1
|
||
|
try:
|
||
|
with self as rv:
|
||
|
yield rv
|
||
|
finally:
|
||
|
if not cleanup:
|
||
|
self._depth -= 1
|
||
|
|
||
|
@property
|
||
|
def meta(self):
|
||
|
"""This is a dictionary which is shared with all the contexts
|
||
|
that are nested. It exists so that click utiltiies can store some
|
||
|
state here if they need to. It is however the responsibility of
|
||
|
that code to manage this dictionary well.
|
||
|
|
||
|
The keys are supposed to be unique dotted strings. For instance
|
||
|
module paths are a good choice for it. What is stored in there is
|
||
|
irrelevant for the operation of click. However what is important is
|
||
|
that code that places data here adheres to the general semantics of
|
||
|
the system.
|
||
|
|
||
|
Example usage::
|
||
|
|
||
|
LANG_KEY = __name__ + '.lang'
|
||
|
|
||
|
def set_language(value):
|
||
|
ctx = get_current_context()
|
||
|
ctx.meta[LANG_KEY] = value
|
||
|
|
||
|
def get_language():
|
||
|
return get_current_context().meta.get(LANG_KEY, 'en_US')
|
||
|
|
||
|
.. versionadded:: 5.0
|
||
|
"""
|
||
|
return self._meta
|
||
|
|
||
|
def make_formatter(self):
|
||
|
"""Creates the formatter for the help and usage output."""
|
||
|
return HelpFormatter(width=self.terminal_width,
|
||
|
max_width=self.max_content_width)
|
||
|
|
||
|
def call_on_close(self, f):
|
||
|
"""This decorator remembers a function as callback that should be
|
||
|
executed when the context tears down. This is most useful to bind
|
||
|
resource handling to the script execution. For instance, file objects
|
||
|
opened by the :class:`File` type will register their close callbacks
|
||
|
here.
|
||
|
|
||
|
:param f: the function to execute on teardown.
|
||
|
"""
|
||
|
self._close_callbacks.append(f)
|
||
|
return f
|
||
|
|
||
|
def close(self):
|
||
|
"""Invokes all close callbacks."""
|
||
|
for cb in self._close_callbacks:
|
||
|
cb()
|
||
|
self._close_callbacks = []
|
||
|
|
||
|
@property
|
||
|
def command_path(self):
|
||
|
"""The computed command path. This is used for the ``usage``
|
||
|
information on the help page. It's automatically created by
|
||
|
combining the info names of the chain of contexts to the root.
|
||
|
"""
|
||
|
rv = ''
|
||
|
if self.info_name is not None:
|
||
|
rv = self.info_name
|
||
|
if self.parent is not None:
|
||
|
rv = self.parent.command_path + ' ' + rv
|
||
|
return rv.lstrip()
|
||
|
|
||
|
def find_root(self):
|
||
|
"""Finds the outermost context."""
|
||
|
node = self
|
||
|
while node.parent is not None:
|
||
|
node = node.parent
|
||
|
return node
|
||
|
|
||
|
def find_object(self, object_type):
|
||
|
"""Finds the closest object of a given type."""
|
||
|
node = self
|
||
|
while node is not None:
|
||
|
if isinstance(node.obj, object_type):
|
||
|
return node.obj
|
||
|
node = node.parent
|
||
|
|
||
|
def ensure_object(self, object_type):
|
||
|
"""Like :meth:`find_object` but sets the innermost object to a
|
||
|
new instance of `object_type` if it does not exist.
|
||
|
"""
|
||
|
rv = self.find_object(object_type)
|
||
|
if rv is None:
|
||
|
self.obj = rv = object_type()
|
||
|
return rv
|
||
|
|
||
|
def lookup_default(self, name):
|
||
|
"""Looks up the default for a parameter name. This by default
|
||
|
looks into the :attr:`default_map` if available.
|
||
|
"""
|
||
|
if self.default_map is not None:
|
||
|
rv = self.default_map.get(name)
|
||
|
if callable(rv):
|
||
|
rv = rv()
|
||
|
return rv
|
||
|
|
||
|
def fail(self, message):
|
||
|
"""Aborts the execution of the program with a specific error
|
||
|
message.
|
||
|
|
||
|
:param message: the error message to fail with.
|
||
|
"""
|
||
|
raise UsageError(message, self)
|
||
|
|
||
|
def abort(self):
|
||
|
"""Aborts the script."""
|
||
|
raise Abort()
|
||
|
|
||
|
def exit(self, code=0):
|
||
|
"""Exits the application with a given exit code."""
|
||
|
sys.exit(code)
|
||
|
|
||
|
def get_usage(self):
|
||
|
"""Helper method to get formatted usage string for the current
|
||
|
context and command.
|
||
|
"""
|
||
|
return self.command.get_usage(self)
|
||
|
|
||
|
def get_help(self):
|
||
|
"""Helper method to get formatted help page for the current
|
||
|
context and command.
|
||
|
"""
|
||
|
return self.command.get_help(self)
|
||
|
|
||
|
def invoke(*args, **kwargs):
|
||
|
"""Invokes a command callback in exactly the way it expects. There
|
||
|
are two ways to invoke this method:
|
||
|
|
||
|
1. the first argument can be a callback and all other arguments and
|
||
|
keyword arguments are forwarded directly to the function.
|
||
|
2. the first argument is a click command object. In that case all
|
||
|
arguments are forwarded as well but proper click parameters
|
||
|
(options and click arguments) must be keyword arguments and Click
|
||
|
will fill in defaults.
|
||
|
|
||
|
Note that before Click 3.2 keyword arguments were not properly filled
|
||
|
in against the intention of this code and no context was created. For
|
||
|
more information about this change and why it was done in a bugfix
|
||
|
release see :ref:`upgrade-to-3.2`.
|
||
|
"""
|
||
|
self, callback = args[:2]
|
||
|
ctx = self
|
||
|
|
||
|
# It's also possible to invoke another command which might or
|
||
|
# might not have a callback. In that case we also fill
|
||
|
# in defaults and make a new context for this command.
|
||
|
if isinstance(callback, Command):
|
||
|
other_cmd = callback
|
||
|
callback = other_cmd.callback
|
||
|
ctx = Context(other_cmd, info_name=other_cmd.name, parent=self)
|
||
|
if callback is None:
|
||
|
raise TypeError('The given command does not have a '
|
||
|
'callback that can be invoked.')
|
||
|
|
||
|
for param in other_cmd.params:
|
||
|
if param.name not in kwargs and param.expose_value:
|
||
|
kwargs[param.name] = param.get_default(ctx)
|
||
|
|
||
|
args = args[2:]
|
||
|
with augment_usage_errors(self):
|
||
|
with ctx:
|
||
|
return callback(*args, **kwargs)
|
||
|
|
||
|
def forward(*args, **kwargs):
|
||
|
"""Similar to :meth:`invoke` but fills in default keyword
|
||
|
arguments from the current context if the other command expects
|
||
|
it. This cannot invoke callbacks directly, only other commands.
|
||
|
"""
|
||
|
self, cmd = args[:2]
|
||
|
|
||
|
# It's also possible to invoke another command which might or
|
||
|
# might not have a callback.
|
||
|
if not isinstance(cmd, Command):
|
||
|
raise TypeError('Callback is not a command.')
|
||
|
|
||
|
for param in self.params:
|
||
|
if param not in kwargs:
|
||
|
kwargs[param] = self.params[param]
|
||
|
|
||
|
return self.invoke(cmd, **kwargs)
|
||
|
|
||
|
|
||
|
class BaseCommand(object):
|
||
|
"""The base command implements the minimal API contract of commands.
|
||
|
Most code will never use this as it does not implement a lot of useful
|
||
|
functionality but it can act as the direct subclass of alternative
|
||
|
parsing methods that do not depend on the Click parser.
|
||
|
|
||
|
For instance, this can be used to bridge Click and other systems like
|
||
|
argparse or docopt.
|
||
|
|
||
|
Because base commands do not implement a lot of the API that other
|
||
|
parts of Click take for granted, they are not supported for all
|
||
|
operations. For instance, they cannot be used with the decorators
|
||
|
usually and they have no built-in callback system.
|
||
|
|
||
|
.. versionchanged:: 2.0
|
||
|
Added the `context_settings` parameter.
|
||
|
|
||
|
:param name: the name of the command to use unless a group overrides it.
|
||
|
:param context_settings: an optional dictionary with defaults that are
|
||
|
passed to the context object.
|
||
|
"""
|
||
|
#: the default for the :attr:`Context.allow_extra_args` flag.
|
||
|
allow_extra_args = False
|
||
|
#: the default for the :attr:`Context.allow_interspersed_args` flag.
|
||
|
allow_interspersed_args = True
|
||
|
#: the default for the :attr:`Context.ignore_unknown_options` flag.
|
||
|
ignore_unknown_options = False
|
||
|
|
||
|
def __init__(self, name, context_settings=None):
|
||
|
#: the name the command thinks it has. Upon registering a command
|
||
|
#: on a :class:`Group` the group will default the command name
|
||
|
#: with this information. You should instead use the
|
||
|
#: :class:`Context`\'s :attr:`~Context.info_name` attribute.
|
||
|
self.name = name
|
||
|
if context_settings is None:
|
||
|
context_settings = {}
|
||
|
#: an optional dictionary with defaults passed to the context.
|
||
|
self.context_settings = context_settings
|
||
|
|
||
|
def get_usage(self, ctx):
|
||
|
raise NotImplementedError('Base commands cannot get usage')
|
||
|
|
||
|
def get_help(self, ctx):
|
||
|
raise NotImplementedError('Base commands cannot get help')
|
||
|
|
||
|
def make_context(self, info_name, args, parent=None, **extra):
|
||
|
"""This function when given an info name and arguments will kick
|
||
|
off the parsing and create a new :class:`Context`. It does not
|
||
|
invoke the actual command callback though.
|
||
|
|
||
|
:param info_name: the info name for this invokation. Generally this
|
||
|
is the most descriptive name for the script or
|
||
|
command. For the toplevel script it's usually
|
||
|
the name of the script, for commands below it it's
|
||
|
the name of the script.
|
||
|
:param args: the arguments to parse as list of strings.
|
||
|
:param parent: the parent context if available.
|
||
|
:param extra: extra keyword arguments forwarded to the context
|
||
|
constructor.
|
||
|
"""
|
||
|
for key, value in iteritems(self.context_settings):
|
||
|
if key not in extra:
|
||
|
extra[key] = value
|
||
|
ctx = Context(self, info_name=info_name, parent=parent, **extra)
|
||
|
with ctx.scope(cleanup=False):
|
||
|
self.parse_args(ctx, args)
|
||
|
return ctx
|
||
|
|
||
|
def parse_args(self, ctx, args):
|
||
|
"""Given a context and a list of arguments this creates the parser
|
||
|
and parses the arguments, then modifies the context as necessary.
|
||
|
This is automatically invoked by :meth:`make_context`.
|
||
|
"""
|
||
|
raise NotImplementedError('Base commands do not know how to parse '
|
||
|
'arguments.')
|
||
|
|
||
|
def invoke(self, ctx):
|
||
|
"""Given a context, this invokes the command. The default
|
||
|
implementation is raising a not implemented error.
|
||
|
"""
|
||
|
raise NotImplementedError('Base commands are not invokable by default')
|
||
|
|
||
|
def main(self, args=None, prog_name=None, complete_var=None,
|
||
|
standalone_mode=True, **extra):
|
||
|
"""This is the way to invoke a script with all the bells and
|
||
|
whistles as a command line application. This will always terminate
|
||
|
the application after a call. If this is not wanted, ``SystemExit``
|
||
|
needs to be caught.
|
||
|
|
||
|
This method is also available by directly calling the instance of
|
||
|
a :class:`Command`.
|
||
|
|
||
|
.. versionadded:: 3.0
|
||
|
Added the `standalone_mode` flag to control the standalone mode.
|
||
|
|
||
|
:param args: the arguments that should be used for parsing. If not
|
||
|
provided, ``sys.argv[1:]`` is used.
|
||
|
:param prog_name: the program name that should be used. By default
|
||
|
the program name is constructed by taking the file
|
||
|
name from ``sys.argv[0]``.
|
||
|
:param complete_var: the environment variable that controls the
|
||
|
bash completion support. The default is
|
||
|
``"_<prog_name>_COMPLETE"`` with prog name in
|
||
|
uppercase.
|
||
|
:param standalone_mode: the default behavior is to invoke the script
|
||
|
in standalone mode. Click will then
|
||
|
handle exceptions and convert them into
|
||
|
error messages and the function will never
|
||
|
return but shut down the interpreter. If
|
||
|
this is set to `False` they will be
|
||
|
propagated to the caller and the return
|
||
|
value of this function is the return value
|
||
|
of :meth:`invoke`.
|
||
|
:param extra: extra keyword arguments are forwarded to the context
|
||
|
constructor. See :class:`Context` for more information.
|
||
|
"""
|
||
|
# If we are in Python 3, we will verify that the environment is
|
||
|
# sane at this point of reject further execution to avoid a
|
||
|
# broken script.
|
||
|
if not PY2:
|
||
|
_verify_python3_env()
|
||
|
else:
|
||
|
_check_for_unicode_literals()
|
||
|
|
||
|
if args is None:
|
||
|
args = get_os_args()
|
||
|
else:
|
||
|
args = list(args)
|
||
|
|
||
|
if prog_name is None:
|
||
|
prog_name = make_str(os.path.basename(
|
||
|
sys.argv and sys.argv[0] or __file__))
|
||
|
|
||
|
# Hook for the Bash completion. This only activates if the Bash
|
||
|
# completion is actually enabled, otherwise this is quite a fast
|
||
|
# noop.
|
||
|
_bashcomplete(self, prog_name, complete_var)
|
||
|
|
||
|
try:
|
||
|
try:
|
||
|
with self.make_context(prog_name, args, **extra) as ctx:
|
||
|
rv = self.invoke(ctx)
|
||
|
if not standalone_mode:
|
||
|
return rv
|
||
|
ctx.exit()
|
||
|
except (EOFError, KeyboardInterrupt):
|
||
|
echo(file=sys.stderr)
|
||
|
raise Abort()
|
||
|
except ClickException as e:
|
||
|
if not standalone_mode:
|
||
|
raise
|
||
|
e.show()
|
||
|
sys.exit(e.exit_code)
|
||
|
except Abort:
|
||
|
if not standalone_mode:
|
||
|
raise
|
||
|
echo('Aborted!', file=sys.stderr)
|
||
|
sys.exit(1)
|
||
|
|
||
|
def __call__(self, *args, **kwargs):
|
||
|
"""Alias for :meth:`main`."""
|
||
|
return self.main(*args, **kwargs)
|
||
|
|
||
|
|
||
|
class Command(BaseCommand):
|
||
|
"""Commands are the basic building block of command line interfaces in
|
||
|
Click. A basic command handles command line parsing and might dispatch
|
||
|
more parsing to commands nested below it.
|
||
|
|
||
|
.. versionchanged:: 2.0
|
||
|
Added the `context_settings` parameter.
|
||
|
|
||
|
:param name: the name of the command to use unless a group overrides it.
|
||
|
:param context_settings: an optional dictionary with defaults that are
|
||
|
passed to the context object.
|
||
|
:param callback: the callback to invoke. This is optional.
|
||
|
:param params: the parameters to register with this command. This can
|
||
|
be either :class:`Option` or :class:`Argument` objects.
|
||
|
:param help: the help string to use for this command.
|
||
|
:param epilog: like the help string but it's printed at the end of the
|
||
|
help page after everything else.
|
||
|
:param short_help: the short help to use for this command. This is
|
||
|
shown on the command listing of the parent command.
|
||
|
:param add_help_option: by default each command registers a ``--help``
|
||
|
option. This can be disabled by this parameter.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, name, context_settings=None, callback=None,
|
||
|
params=None, help=None, epilog=None, short_help=None,
|
||
|
options_metavar='[OPTIONS]', add_help_option=True):
|
||
|
BaseCommand.__init__(self, name, context_settings)
|
||
|
#: the callback to execute when the command fires. This might be
|
||
|
#: `None` in which case nothing happens.
|
||
|
self.callback = callback
|
||
|
#: the list of parameters for this command in the order they
|
||
|
#: should show up in the help page and execute. Eager parameters
|
||
|
#: will automatically be handled before non eager ones.
|
||
|
self.params = params or []
|
||
|
self.help = help
|
||
|
self.epilog = epilog
|
||
|
self.options_metavar = options_metavar
|
||
|
if short_help is None and help:
|
||
|
short_help = make_default_short_help(help)
|
||
|
self.short_help = short_help
|
||
|
self.add_help_option = add_help_option
|
||
|
|
||
|
def get_usage(self, ctx):
|
||
|
formatter = ctx.make_formatter()
|
||
|
self.format_usage(ctx, formatter)
|
||
|
return formatter.getvalue().rstrip('\n')
|
||
|
|
||
|
def get_params(self, ctx):
|
||
|
rv = self.params
|
||
|
help_option = self.get_help_option(ctx)
|
||
|
if help_option is not None:
|
||
|
rv = rv + [help_option]
|
||
|
return rv
|
||
|
|
||
|
def format_usage(self, ctx, formatter):
|
||
|
"""Writes the usage line into the formatter."""
|
||
|
pieces = self.collect_usage_pieces(ctx)
|
||
|
formatter.write_usage(ctx.command_path, ' '.join(pieces))
|
||
|
|
||
|
def collect_usage_pieces(self, ctx):
|
||
|
"""Returns all the pieces that go into the usage line and returns
|
||
|
it as a list of strings.
|
||
|
"""
|
||
|
rv = [self.options_metavar]
|
||
|
for param in self.get_params(ctx):
|
||
|
rv.extend(param.get_usage_pieces(ctx))
|
||
|
return rv
|
||
|
|
||
|
def get_help_option_names(self, ctx):
|
||
|
"""Returns the names for the help option."""
|
||
|
all_names = set(ctx.help_option_names)
|
||
|
for param in self.params:
|
||
|
all_names.difference_update(param.opts)
|
||
|
all_names.difference_update(param.secondary_opts)
|
||
|
return all_names
|
||
|
|
||
|
def get_help_option(self, ctx):
|
||
|
"""Returns the help option object."""
|
||
|
help_options = self.get_help_option_names(ctx)
|
||
|
if not help_options or not self.add_help_option:
|
||
|
return
|
||
|
|
||
|
def show_help(ctx, param, value):
|
||
|
if value and not ctx.resilient_parsing:
|
||
|
echo(ctx.get_help(), color=ctx.color)
|
||
|
ctx.exit()
|
||
|
return Option(help_options, is_flag=True,
|
||
|
is_eager=True, expose_value=False,
|
||
|
callback=show_help,
|
||
|
help='Show this message and exit.')
|
||
|
|
||
|
def make_parser(self, ctx):
|
||
|
"""Creates the underlying option parser for this command."""
|
||
|
parser = OptionParser(ctx)
|
||
|
parser.allow_interspersed_args = ctx.allow_interspersed_args
|
||
|
parser.ignore_unknown_options = ctx.ignore_unknown_options
|
||
|
for param in self.get_params(ctx):
|
||
|
param.add_to_parser(parser, ctx)
|
||
|
return parser
|
||
|
|
||
|
def get_help(self, ctx):
|
||
|
"""Formats the help into a string and returns it. This creates a
|
||
|
formatter and will call into the following formatting methods:
|
||
|
"""
|
||
|
formatter = ctx.make_formatter()
|
||
|
self.format_help(ctx, formatter)
|
||
|
return formatter.getvalue().rstrip('\n')
|
||
|
|
||
|
def format_help(self, ctx, formatter):
|
||
|
"""Writes the help into the formatter if it exists.
|
||
|
|
||
|
This calls into the following methods:
|
||
|
|
||
|
- :meth:`format_usage`
|
||
|
- :meth:`format_help_text`
|
||
|
- :meth:`format_options`
|
||
|
- :meth:`format_epilog`
|
||
|
"""
|
||
|
self.format_usage(ctx, formatter)
|
||
|
self.format_help_text(ctx, formatter)
|
||
|
self.format_options(ctx, formatter)
|
||
|
self.format_epilog(ctx, formatter)
|
||
|
|
||
|
def format_help_text(self, ctx, formatter):
|
||
|
"""Writes the help text to the formatter if it exists."""
|
||
|
if self.help:
|
||
|
formatter.write_paragraph()
|
||
|
with formatter.indentation():
|
||
|
formatter.write_text(self.help)
|
||
|
|
||
|
def format_options(self, ctx, formatter):
|
||
|
"""Writes all the options into the formatter if they exist."""
|
||
|
opts = []
|
||
|
for param in self.get_params(ctx):
|
||
|
rv = param.get_help_record(ctx)
|
||
|
if rv is not None:
|
||
|
opts.append(rv)
|
||
|
|
||
|
if opts:
|
||
|
with formatter.section('Options'):
|
||
|
formatter.write_dl(opts)
|
||
|
|
||
|
def format_epilog(self, ctx, formatter):
|
||
|
"""Writes the epilog into the formatter if it exists."""
|
||
|
if self.epilog:
|
||
|
formatter.write_paragraph()
|
||
|
with formatter.indentation():
|
||
|
formatter.write_text(self.epilog)
|
||
|
|
||
|
def parse_args(self, ctx, args):
|
||
|
parser = self.make_parser(ctx)
|
||
|
opts, args, param_order = parser.parse_args(args=args)
|
||
|
|
||
|
for param in iter_params_for_processing(
|
||
|
param_order, self.get_params(ctx)):
|
||
|
value, args = param.handle_parse_result(ctx, opts, args)
|
||
|
|
||
|
if args and not ctx.allow_extra_args and not ctx.resilient_parsing:
|
||
|
ctx.fail('Got unexpected extra argument%s (%s)'
|
||
|
% (len(args) != 1 and 's' or '',
|
||
|
' '.join(map(make_str, args))))
|
||
|
|
||
|
ctx.args = args
|
||
|
return args
|
||
|
|
||
|
def invoke(self, ctx):
|
||
|
"""Given a context, this invokes the attached callback (if it exists)
|
||
|
in the right way.
|
||
|
"""
|
||
|
if self.callback is not None:
|
||
|
return ctx.invoke(self.callback, **ctx.params)
|
||
|
|
||
|
|
||
|
class MultiCommand(Command):
|
||
|
"""A multi command is the basic implementation of a command that
|
||
|
dispatches to subcommands. The most common version is the
|
||
|
:class:`Group`.
|
||
|
|
||
|
:param invoke_without_command: this controls how the multi command itself
|
||
|
is invoked. By default it's only invoked
|
||
|
if a subcommand is provided.
|
||
|
:param no_args_is_help: this controls what happens if no arguments are
|
||
|
provided. This option is enabled by default if
|
||
|
`invoke_without_command` is disabled or disabled
|
||
|
if it's enabled. If enabled this will add
|
||
|
``--help`` as argument if no arguments are
|
||
|
passed.
|
||
|
:param subcommand_metavar: the string that is used in the documentation
|
||
|
to indicate the subcommand place.
|
||
|
:param chain: if this is set to `True` chaining of multiple subcommands
|
||
|
is enabled. This restricts the form of commands in that
|
||
|
they cannot have optional arguments but it allows
|
||
|
multiple commands to be chained together.
|
||
|
:param result_callback: the result callback to attach to this multi
|
||
|
command.
|
||
|
"""
|
||
|
allow_extra_args = True
|
||
|
allow_interspersed_args = False
|
||
|
|
||
|
def __init__(self, name=None, invoke_without_command=False,
|
||
|
no_args_is_help=None, subcommand_metavar=None,
|
||
|
chain=False, result_callback=None, **attrs):
|
||
|
Command.__init__(self, name, **attrs)
|
||
|
if no_args_is_help is None:
|
||
|
no_args_is_help = not invoke_without_command
|
||
|
self.no_args_is_help = no_args_is_help
|
||
|
self.invoke_without_command = invoke_without_command
|
||
|
if subcommand_metavar is None:
|
||
|
if chain:
|
||
|
subcommand_metavar = SUBCOMMANDS_METAVAR
|
||
|
else:
|
||
|
subcommand_metavar = SUBCOMMAND_METAVAR
|
||
|
self.subcommand_metavar = subcommand_metavar
|
||
|
self.chain = chain
|
||
|
#: The result callback that is stored. This can be set or
|
||
|
#: overridden with the :func:`resultcallback` decorator.
|
||
|
self.result_callback = result_callback
|
||
|
|
||
|
if self.chain:
|
||
|
for param in self.params:
|
||
|
if isinstance(param, Argument) and not param.required:
|
||
|
raise RuntimeError('Multi commands in chain mode cannot '
|
||
|
'have optional arguments.')
|
||
|
|
||
|
def collect_usage_pieces(self, ctx):
|
||
|
rv = Command.collect_usage_pieces(self, ctx)
|
||
|
rv.append(self.subcommand_metavar)
|
||
|
return rv
|
||
|
|
||
|
def format_options(self, ctx, formatter):
|
||
|
Command.format_options(self, ctx, formatter)
|
||
|
self.format_commands(ctx, formatter)
|
||
|
|
||
|
def resultcallback(self, replace=False):
|
||
|
"""Adds a result callback to the chain command. By default if a
|
||
|
result callback is already registered this will chain them but
|
||
|
this can be disabled with the `replace` parameter. The result
|
||
|
callback is invoked with the return value of the subcommand
|
||
|
(or the list of return values from all subcommands if chaining
|
||
|
is enabled) as well as the parameters as they would be passed
|
||
|
to the main callback.
|
||
|
|
||
|
Example::
|
||
|
|
||
|
@click.group()
|
||
|
@click.option('-i', '--input', default=23)
|
||
|
def cli(input):
|
||
|
return 42
|
||
|
|
||
|
@cli.resultcallback()
|
||
|
def process_result(result, input):
|
||
|
return result + input
|
||
|
|
||
|
.. versionadded:: 3.0
|
||
|
|
||
|
:param replace: if set to `True` an already existing result
|
||
|
callback will be removed.
|
||
|
"""
|
||
|
def decorator(f):
|
||
|
old_callback = self.result_callback
|
||
|
if old_callback is None or replace:
|
||
|
self.result_callback = f
|
||
|
return f
|
||
|
def function(__value, *args, **kwargs):
|
||
|
return f(old_callback(__value, *args, **kwargs),
|
||
|
*args, **kwargs)
|
||
|
self.result_callback = rv = update_wrapper(function, f)
|
||
|
return rv
|
||
|
return decorator
|
||
|
|
||
|
def format_commands(self, ctx, formatter):
|
||
|
"""Extra format methods for multi methods that adds all the commands
|
||
|
after the options.
|
||
|
"""
|
||
|
rows = []
|
||
|
for subcommand in self.list_commands(ctx):
|
||
|
cmd = self.get_command(ctx, subcommand)
|
||
|
# What is this, the tool lied about a command. Ignore it
|
||
|
if cmd is None:
|
||
|
continue
|
||
|
|
||
|
help = cmd.short_help or ''
|
||
|
rows.append((subcommand, help))
|
||
|
|
||
|
if rows:
|
||
|
with formatter.section('Commands'):
|
||
|
formatter.write_dl(rows)
|
||
|
|
||
|
def parse_args(self, ctx, args):
|
||
|
if not args and self.no_args_is_help and not ctx.resilient_parsing:
|
||
|
echo(ctx.get_help(), color=ctx.color)
|
||
|
ctx.exit()
|
||
|
|
||
|
rest = Command.parse_args(self, ctx, args)
|
||
|
if self.chain:
|
||
|
ctx.protected_args = rest
|
||
|
ctx.args = []
|
||
|
elif rest:
|
||
|
ctx.protected_args, ctx.args = rest[:1], rest[1:]
|
||
|
|
||
|
return ctx.args
|
||
|
|
||
|
def invoke(self, ctx):
|
||
|
def _process_result(value):
|
||
|
if self.result_callback is not None:
|
||
|
value = ctx.invoke(self.result_callback, value,
|
||
|
**ctx.params)
|
||
|
return value
|
||
|
|
||
|
if not ctx.protected_args:
|
||
|
# If we are invoked without command the chain flag controls
|
||
|
# how this happens. If we are not in chain mode, the return
|
||
|
# value here is the return value of the command.
|
||
|
# If however we are in chain mode, the return value is the
|
||
|
# return value of the result processor invoked with an empty
|
||
|
# list (which means that no subcommand actually was executed).
|
||
|
if self.invoke_without_command:
|
||
|
if not self.chain:
|
||
|
return Command.invoke(self, ctx)
|
||
|
with ctx:
|
||
|
Command.invoke(self, ctx)
|
||
|
return _process_result([])
|
||
|
ctx.fail('Missing command.')
|
||
|
|
||
|
# Fetch args back out
|
||
|
args = ctx.protected_args + ctx.args
|
||
|
ctx.args = []
|
||
|
ctx.protected_args = []
|
||
|
|
||
|
# If we're not in chain mode, we only allow the invocation of a
|
||
|
# single command but we also inform the current context about the
|
||
|
# name of the command to invoke.
|
||
|
if not self.chain:
|
||
|
# Make sure the context is entered so we do not clean up
|
||
|
# resources until the result processor has worked.
|
||
|
with ctx:
|
||
|
cmd_name, cmd, args = self.resolve_command(ctx, args)
|
||
|
ctx.invoked_subcommand = cmd_name
|
||
|
Command.invoke(self, ctx)
|
||
|
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx)
|
||
|
with sub_ctx:
|
||
|
return _process_result(sub_ctx.command.invoke(sub_ctx))
|
||
|
|
||
|
# In chain mode we create the contexts step by step, but after the
|
||
|
# base command has been invoked. Because at that point we do not
|
||
|
# know the subcommands yet, the invoked subcommand attribute is
|
||
|
# set to ``*`` to inform the command that subcommands are executed
|
||
|
# but nothing else.
|
||
|
with ctx:
|
||
|
ctx.invoked_subcommand = args and '*' or None
|
||
|
Command.invoke(self, ctx)
|
||
|
|
||
|
# Otherwise we make every single context and invoke them in a
|
||
|
# chain. In that case the return value to the result processor
|
||
|
# is the list of all invoked subcommand's results.
|
||
|
contexts = []
|
||
|
while args:
|
||
|
cmd_name, cmd, args = self.resolve_command(ctx, args)
|
||
|
sub_ctx = cmd.make_context(cmd_name, args, parent=ctx,
|
||
|
allow_extra_args=True,
|
||
|
allow_interspersed_args=False)
|
||
|
contexts.append(sub_ctx)
|
||
|
args, sub_ctx.args = sub_ctx.args, []
|
||
|
|
||
|
rv = []
|
||
|
for sub_ctx in contexts:
|
||
|
with sub_ctx:
|
||
|
rv.append(sub_ctx.command.invoke(sub_ctx))
|
||
|
return _process_result(rv)
|
||
|
|
||
|
def resolve_command(self, ctx, args):
|
||
|
cmd_name = make_str(args[0])
|
||
|
original_cmd_name = cmd_name
|
||
|
|
||
|
# Get the command
|
||
|
cmd = self.get_command(ctx, cmd_name)
|
||
|
|
||
|
# If we can't find the command but there is a normalization
|
||
|
# function available, we try with that one.
|
||
|
if cmd is None and ctx.token_normalize_func is not None:
|
||
|
cmd_name = ctx.token_normalize_func(cmd_name)
|
||
|
cmd = self.get_command(ctx, cmd_name)
|
||
|
|
||
|
# If we don't find the command we want to show an error message
|
||
|
# to the user that it was not provided. However, there is
|
||
|
# something else we should do: if the first argument looks like
|
||
|
# an option we want to kick off parsing again for arguments to
|
||
|
# resolve things like --help which now should go to the main
|
||
|
# place.
|
||
|
if cmd is None:
|
||
|
if split_opt(cmd_name)[0]:
|
||
|
self.parse_args(ctx, ctx.args)
|
||
|
ctx.fail('No such command "%s".' % original_cmd_name)
|
||
|
|
||
|
return cmd_name, cmd, args[1:]
|
||
|
|
||
|
def get_command(self, ctx, cmd_name):
|
||
|
"""Given a context and a command name, this returns a
|
||
|
:class:`Command` object if it exists or returns `None`.
|
||
|
"""
|
||
|
raise NotImplementedError()
|
||
|
|
||
|
def list_commands(self, ctx):
|
||
|
"""Returns a list of subcommand names in the order they should
|
||
|
appear.
|
||
|
"""
|
||
|
return []
|
||
|
|
||
|
|
||
|
class Group(MultiCommand):
|
||
|
"""A group allows a command to have subcommands attached. This is the
|
||
|
most common way to implement nesting in Click.
|
||
|
|
||
|
:param commands: a dictionary of commands.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, name=None, commands=None, **attrs):
|
||
|
MultiCommand.__init__(self, name, **attrs)
|
||
|
#: the registered subcommands by their exported names.
|
||
|
self.commands = commands or {}
|
||
|
|
||
|
def add_command(self, cmd, name=None):
|
||
|
"""Registers another :class:`Command` with this group. If the name
|
||
|
is not provided, the name of the command is used.
|
||
|
"""
|
||
|
name = name or cmd.name
|
||
|
if name is None:
|
||
|
raise TypeError('Command has no name.')
|
||
|
_check_multicommand(self, name, cmd, register=True)
|
||
|
self.commands[name] = cmd
|
||
|
|
||
|
def command(self, *args, **kwargs):
|
||
|
"""A shortcut decorator for declaring and attaching a command to
|
||
|
the group. This takes the same arguments as :func:`command` but
|
||
|
immediately registers the created command with this instance by
|
||
|
calling into :meth:`add_command`.
|
||
|
"""
|
||
|
def decorator(f):
|
||
|
cmd = command(*args, **kwargs)(f)
|
||
|
self.add_command(cmd)
|
||
|
return cmd
|
||
|
return decorator
|
||
|
|
||
|
def group(self, *args, **kwargs):
|
||
|
"""A shortcut decorator for declaring and attaching a group to
|
||
|
the group. This takes the same arguments as :func:`group` but
|
||
|
immediately registers the created command with this instance by
|
||
|
calling into :meth:`add_command`.
|
||
|
"""
|
||
|
def decorator(f):
|
||
|
cmd = group(*args, **kwargs)(f)
|
||
|
self.add_command(cmd)
|
||
|
return cmd
|
||
|
return decorator
|
||
|
|
||
|
def get_command(self, ctx, cmd_name):
|
||
|
return self.commands.get(cmd_name)
|
||
|
|
||
|
def list_commands(self, ctx):
|
||
|
return sorted(self.commands)
|
||
|
|
||
|
|
||
|
class CommandCollection(MultiCommand):
|
||
|
"""A command collection is a multi command that merges multiple multi
|
||
|
commands together into one. This is a straightforward implementation
|
||
|
that accepts a list of different multi commands as sources and
|
||
|
provides all the commands for each of them.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, name=None, sources=None, **attrs):
|
||
|
MultiCommand.__init__(self, name, **attrs)
|
||
|
#: The list of registered multi commands.
|
||
|
self.sources = sources or []
|
||
|
|
||
|
def add_source(self, multi_cmd):
|
||
|
"""Adds a new multi command to the chain dispatcher."""
|
||
|
self.sources.append(multi_cmd)
|
||
|
|
||
|
def get_command(self, ctx, cmd_name):
|
||
|
for source in self.sources:
|
||
|
rv = source.get_command(ctx, cmd_name)
|
||
|
if rv is not None:
|
||
|
if self.chain:
|
||
|
_check_multicommand(self, cmd_name, rv)
|
||
|
return rv
|
||
|
|
||
|
def list_commands(self, ctx):
|
||
|
rv = set()
|
||
|
for source in self.sources:
|
||
|
rv.update(source.list_commands(ctx))
|
||
|
return sorted(rv)
|
||
|
|
||
|
|
||
|
class Parameter(object):
|
||
|
"""A parameter to a command comes in two versions: they are either
|
||
|
:class:`Option`\s or :class:`Argument`\s. Other subclasses are currently
|
||
|
not supported by design as some of the internals for parsing are
|
||
|
intentionally not finalized.
|
||
|
|
||
|
Some settings are supported by both options and arguments.
|
||
|
|
||
|
.. versionchanged:: 2.0
|
||
|
Changed signature for parameter callback to also be passed the
|
||
|
parameter. In Click 2.0, the old callback format will still work,
|
||
|
but it will raise a warning to give you change to migrate the
|
||
|
code easier.
|
||
|
|
||
|
:param param_decls: the parameter declarations for this option or
|
||
|
argument. This is a list of flags or argument
|
||
|
names.
|
||
|
:param type: the type that should be used. Either a :class:`ParamType`
|
||
|
or a Python type. The later is converted into the former
|
||
|
automatically if supported.
|
||
|
:param required: controls if this is optional or not.
|
||
|
:param default: the default value if omitted. This can also be a callable,
|
||
|
in which case it's invoked when the default is needed
|
||
|
without any arguments.
|
||
|
:param callback: a callback that should be executed after the parameter
|
||
|
was matched. This is called as ``fn(ctx, param,
|
||
|
value)`` and needs to return the value. Before Click
|
||
|
2.0, the signature was ``(ctx, value)``.
|
||
|
:param nargs: the number of arguments to match. If not ``1`` the return
|
||
|
value is a tuple instead of single value. The default for
|
||
|
nargs is ``1`` (except if the type is a tuple, then it's
|
||
|
the arity of the tuple).
|
||
|
:param metavar: how the value is represented in the help page.
|
||
|
:param expose_value: if this is `True` then the value is passed onwards
|
||
|
to the command callback and stored on the context,
|
||
|
otherwise it's skipped.
|
||
|
:param is_eager: eager values are processed before non eager ones. This
|
||
|
should not be set for arguments or it will inverse the
|
||
|
order of processing.
|
||
|
:param envvar: a string or list of strings that are environment variables
|
||
|
that should be checked.
|
||
|
"""
|
||
|
param_type_name = 'parameter'
|
||
|
|
||
|
def __init__(self, param_decls=None, type=None, required=False,
|
||
|
default=None, callback=None, nargs=None, metavar=None,
|
||
|
expose_value=True, is_eager=False, envvar=None):
|
||
|
self.name, self.opts, self.secondary_opts = \
|
||
|
self._parse_decls(param_decls or (), expose_value)
|
||
|
|
||
|
self.type = convert_type(type, default)
|
||
|
|
||
|
# Default nargs to what the type tells us if we have that
|
||
|
# information available.
|
||
|
if nargs is None:
|
||
|
if self.type.is_composite:
|
||
|
nargs = self.type.arity
|
||
|
else:
|
||
|
nargs = 1
|
||
|
|
||
|
self.required = required
|
||
|
self.callback = callback
|
||
|
self.nargs = nargs
|
||
|
self.multiple = False
|
||
|
self.expose_value = expose_value
|
||
|
self.default = default
|
||
|
self.is_eager = is_eager
|
||
|
self.metavar = metavar
|
||
|
self.envvar = envvar
|
||
|
|
||
|
@property
|
||
|
def human_readable_name(self):
|
||
|
"""Returns the human readable name of this parameter. This is the
|
||
|
same as the name for options, but the metavar for arguments.
|
||
|
"""
|
||
|
return self.name
|
||
|
|
||
|
def make_metavar(self):
|
||
|
if self.metavar is not None:
|
||
|
return self.metavar
|
||
|
metavar = self.type.get_metavar(self)
|
||
|
if metavar is None:
|
||
|
metavar = self.type.name.upper()
|
||
|
if self.nargs != 1:
|
||
|
metavar += '...'
|
||
|
return metavar
|
||
|
|
||
|
def get_default(self, ctx):
|
||
|
"""Given a context variable this calculates the default value."""
|
||
|
# Otherwise go with the regular default.
|
||
|
if callable(self.default):
|
||
|
rv = self.default()
|
||
|
else:
|
||
|
rv = self.default
|
||
|
return self.type_cast_value(ctx, rv)
|
||
|
|
||
|
def add_to_parser(self, parser, ctx):
|
||
|
pass
|
||
|
|
||
|
def consume_value(self, ctx, opts):
|
||
|
value = opts.get(self.name)
|
||
|
if value is None:
|
||
|
value = ctx.lookup_default(self.name)
|
||
|
if value is None:
|
||
|
value = self.value_from_envvar(ctx)
|
||
|
return value
|
||
|
|
||
|
def type_cast_value(self, ctx, value):
|
||
|
"""Given a value this runs it properly through the type system.
|
||
|
This automatically handles things like `nargs` and `multiple` as
|
||
|
well as composite types.
|
||
|
"""
|
||
|
if self.type.is_composite:
|
||
|
if self.nargs <= 1:
|
||
|
raise TypeError('Attempted to invoke composite type '
|
||
|
'but nargs has been set to %s. This is '
|
||
|
'not supported; nargs needs to be set to '
|
||
|
'a fixed value > 1.' % self.nargs)
|
||
|
if self.multiple:
|
||
|
return tuple(self.type(x or (), self, ctx) for x in value or ())
|
||
|
return self.type(value or (), self, ctx)
|
||
|
|
||
|
def _convert(value, level):
|
||
|
if level == 0:
|
||
|
return self.type(value, self, ctx)
|
||
|
return tuple(_convert(x, level - 1) for x in value or ())
|
||
|
return _convert(value, (self.nargs != 1) + bool(self.multiple))
|
||
|
|
||
|
def process_value(self, ctx, value):
|
||
|
"""Given a value and context this runs the logic to convert the
|
||
|
value as necessary.
|
||
|
"""
|
||
|
# If the value we were given is None we do nothing. This way
|
||
|
# code that calls this can easily figure out if something was
|
||
|
# not provided. Otherwise it would be converted into an empty
|
||
|
# tuple for multiple invocations which is inconvenient.
|
||
|
if value is not None:
|
||
|
return self.type_cast_value(ctx, value)
|
||
|
|
||
|
def value_is_missing(self, value):
|
||
|
if value is None:
|
||
|
return True
|
||
|
if (self.nargs != 1 or self.multiple) and value == ():
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def full_process_value(self, ctx, value):
|
||
|
value = self.process_value(ctx, value)
|
||
|
|
||
|
if value is None:
|
||
|
value = self.get_default(ctx)
|
||
|
|
||
|
if self.required and self.value_is_missing(value):
|
||
|
raise MissingParameter(ctx=ctx, param=self)
|
||
|
|
||
|
return value
|
||
|
|
||
|
def resolve_envvar_value(self, ctx):
|
||
|
if self.envvar is None:
|
||
|
return
|
||
|
if isinstance(self.envvar, (tuple, list)):
|
||
|
for envvar in self.envvar:
|
||
|
rv = os.environ.get(envvar)
|
||
|
if rv is not None:
|
||
|
return rv
|
||
|
else:
|
||
|
return os.environ.get(self.envvar)
|
||
|
|
||
|
def value_from_envvar(self, ctx):
|
||
|
rv = self.resolve_envvar_value(ctx)
|
||
|
if rv is not None and self.nargs != 1:
|
||
|
rv = self.type.split_envvar_value(rv)
|
||
|
return rv
|
||
|
|
||
|
def handle_parse_result(self, ctx, opts, args):
|
||
|
with augment_usage_errors(ctx, param=self):
|
||
|
value = self.consume_value(ctx, opts)
|
||
|
try:
|
||
|
value = self.full_process_value(ctx, value)
|
||
|
except Exception:
|
||
|
if not ctx.resilient_parsing:
|
||
|
raise
|
||
|
value = None
|
||
|
if self.callback is not None:
|
||
|
try:
|
||
|
value = invoke_param_callback(
|
||
|
self.callback, ctx, self, value)
|
||
|
except Exception:
|
||
|
if not ctx.resilient_parsing:
|
||
|
raise
|
||
|
|
||
|
if self.expose_value:
|
||
|
ctx.params[self.name] = value
|
||
|
return value, args
|
||
|
|
||
|
def get_help_record(self, ctx):
|
||
|
pass
|
||
|
|
||
|
def get_usage_pieces(self, ctx):
|
||
|
return []
|
||
|
|
||
|
|
||
|
class Option(Parameter):
|
||
|
"""Options are usually optional values on the command line and
|
||
|
have some extra features that arguments don't have.
|
||
|
|
||
|
All other parameters are passed onwards to the parameter constructor.
|
||
|
|
||
|
:param show_default: controls if the default value should be shown on the
|
||
|
help page. Normally, defaults are not shown.
|
||
|
:param prompt: if set to `True` or a non empty string then the user will
|
||
|
be prompted for input if not set. If set to `True` the
|
||
|
prompt will be the option name capitalized.
|
||
|
:param confirmation_prompt: if set then the value will need to be confirmed
|
||
|
if it was prompted for.
|
||
|
:param hide_input: if this is `True` then the input on the prompt will be
|
||
|
hidden from the user. This is useful for password
|
||
|
input.
|
||
|
:param is_flag: forces this option to act as a flag. The default is
|
||
|
auto detection.
|
||
|
:param flag_value: which value should be used for this flag if it's
|
||
|
enabled. This is set to a boolean automatically if
|
||
|
the option string contains a slash to mark two options.
|
||
|
:param multiple: if this is set to `True` then the argument is accepted
|
||
|
multiple times and recorded. This is similar to ``nargs``
|
||
|
in how it works but supports arbitrary number of
|
||
|
arguments.
|
||
|
:param count: this flag makes an option increment an integer.
|
||
|
:param allow_from_autoenv: if this is enabled then the value of this
|
||
|
parameter will be pulled from an environment
|
||
|
variable in case a prefix is defined on the
|
||
|
context.
|
||
|
:param help: the help string.
|
||
|
"""
|
||
|
param_type_name = 'option'
|
||
|
|
||
|
def __init__(self, param_decls=None, show_default=False,
|
||
|
prompt=False, confirmation_prompt=False,
|
||
|
hide_input=False, is_flag=None, flag_value=None,
|
||
|
multiple=False, count=False, allow_from_autoenv=True,
|
||
|
type=None, help=None, **attrs):
|
||
|
default_is_missing = attrs.get('default', _missing) is _missing
|
||
|
Parameter.__init__(self, param_decls, type=type, **attrs)
|
||
|
|
||
|
if prompt is True:
|
||
|
prompt_text = self.name.replace('_', ' ').capitalize()
|
||
|
elif prompt is False:
|
||
|
prompt_text = None
|
||
|
else:
|
||
|
prompt_text = prompt
|
||
|
self.prompt = prompt_text
|
||
|
self.confirmation_prompt = confirmation_prompt
|
||
|
self.hide_input = hide_input
|
||
|
|
||
|
# Flags
|
||
|
if is_flag is None:
|
||
|
if flag_value is not None:
|
||
|
is_flag = True
|
||
|
else:
|
||
|
is_flag = bool(self.secondary_opts)
|
||
|
if is_flag and default_is_missing:
|
||
|
self.default = False
|
||
|
if flag_value is None:
|
||
|
flag_value = not self.default
|
||
|
self.is_flag = is_flag
|
||
|
self.flag_value = flag_value
|
||
|
if self.is_flag and isinstance(self.flag_value, bool) \
|
||
|
and type is None:
|
||
|
self.type = BOOL
|
||
|
self.is_bool_flag = True
|
||
|
else:
|
||
|
self.is_bool_flag = False
|
||
|
|
||
|
# Counting
|
||
|
self.count = count
|
||
|
if count:
|
||
|
if type is None:
|
||
|
self.type = IntRange(min=0)
|
||
|
if default_is_missing:
|
||
|
self.default = 0
|
||
|
|
||
|
self.multiple = multiple
|
||
|
self.allow_from_autoenv = allow_from_autoenv
|
||
|
self.help = help
|
||
|
self.show_default = show_default
|
||
|
|
||
|
# Sanity check for stuff we don't support
|
||
|
if __debug__:
|
||
|
if self.nargs < 0:
|
||
|
raise TypeError('Options cannot have nargs < 0')
|
||
|
if self.prompt and self.is_flag and not self.is_bool_flag:
|
||
|
raise TypeError('Cannot prompt for flags that are not bools.')
|
||
|
if not self.is_bool_flag and self.secondary_opts:
|
||
|
raise TypeError('Got secondary option for non boolean flag.')
|
||
|
if self.is_bool_flag and self.hide_input \
|
||
|
and self.prompt is not None:
|
||
|
raise TypeError('Hidden input does not work with boolean '
|
||
|
'flag prompts.')
|
||
|
if self.count:
|
||
|
if self.multiple:
|
||
|
raise TypeError('Options cannot be multiple and count '
|
||
|
'at the same time.')
|
||
|
elif self.is_flag:
|
||
|
raise TypeError('Options cannot be count and flags at '
|
||
|
'the same time.')
|
||
|
|
||
|
def _parse_decls(self, decls, expose_value):
|
||
|
opts = []
|
||
|
secondary_opts = []
|
||
|
name = None
|
||
|
possible_names = []
|
||
|
|
||
|
for decl in decls:
|
||
|
if isidentifier(decl):
|
||
|
if name is not None:
|
||
|
raise TypeError('Name defined twice')
|
||
|
name = decl
|
||
|
else:
|
||
|
split_char = decl[:1] == '/' and ';' or '/'
|
||
|
if split_char in decl:
|
||
|
first, second = decl.split(split_char, 1)
|
||
|
first = first.rstrip()
|
||
|
if first:
|
||
|
possible_names.append(split_opt(first))
|
||
|
opts.append(first)
|
||
|
second = second.lstrip()
|
||
|
if second:
|
||
|
secondary_opts.append(second.lstrip())
|
||
|
else:
|
||
|
possible_names.append(split_opt(decl))
|
||
|
opts.append(decl)
|
||
|
|
||
|
if name is None and possible_names:
|
||
|
possible_names.sort(key=lambda x: len(x[0]))
|
||
|
name = possible_names[-1][1].replace('-', '_').lower()
|
||
|
if not isidentifier(name):
|
||
|
name = None
|
||
|
|
||
|
if name is None:
|
||
|
if not expose_value:
|
||
|
return None, opts, secondary_opts
|
||
|
raise TypeError('Could not determine name for option')
|
||
|
|
||
|
if not opts and not secondary_opts:
|
||
|
raise TypeError('No options defined but a name was passed (%s). '
|
||
|
'Did you mean to declare an argument instead '
|
||
|
'of an option?' % name)
|
||
|
|
||
|
return name, opts, secondary_opts
|
||
|
|
||
|
def add_to_parser(self, parser, ctx):
|
||
|
kwargs = {
|
||
|
'dest': self.name,
|
||
|
'nargs': self.nargs,
|
||
|
'obj': self,
|
||
|
}
|
||
|
|
||
|
if self.multiple:
|
||
|
action = 'append'
|
||
|
elif self.count:
|
||
|
action = 'count'
|
||
|
else:
|
||
|
action = 'store'
|
||
|
|
||
|
if self.is_flag:
|
||
|
kwargs.pop('nargs', None)
|
||
|
if self.is_bool_flag and self.secondary_opts:
|
||
|
parser.add_option(self.opts, action=action + '_const',
|
||
|
const=True, **kwargs)
|
||
|
parser.add_option(self.secondary_opts, action=action +
|
||
|
'_const', const=False, **kwargs)
|
||
|
else:
|
||
|
parser.add_option(self.opts, action=action + '_const',
|
||
|
const=self.flag_value,
|
||
|
**kwargs)
|
||
|
else:
|
||
|
kwargs['action'] = action
|
||
|
parser.add_option(self.opts, **kwargs)
|
||
|
|
||
|
def get_help_record(self, ctx):
|
||
|
any_prefix_is_slash = []
|
||
|
|
||
|
def _write_opts(opts):
|
||
|
rv, any_slashes = join_options(opts)
|
||
|
if any_slashes:
|
||
|
any_prefix_is_slash[:] = [True]
|
||
|
if not self.is_flag and not self.count:
|
||
|
rv += ' ' + self.make_metavar()
|
||
|
return rv
|
||
|
|
||
|
rv = [_write_opts(self.opts)]
|
||
|
if self.secondary_opts:
|
||
|
rv.append(_write_opts(self.secondary_opts))
|
||
|
|
||
|
help = self.help or ''
|
||
|
extra = []
|
||
|
if self.default is not None and self.show_default:
|
||
|
extra.append('default: %s' % (
|
||
|
', '.join('%s' % d for d in self.default)
|
||
|
if isinstance(self.default, (list, tuple))
|
||
|
else self.default, ))
|
||
|
if self.required:
|
||
|
extra.append('required')
|
||
|
if extra:
|
||
|
help = '%s[%s]' % (help and help + ' ' or '', '; '.join(extra))
|
||
|
|
||
|
return ((any_prefix_is_slash and '; ' or ' / ').join(rv), help)
|
||
|
|
||
|
def get_default(self, ctx):
|
||
|
# If we're a non boolean flag out default is more complex because
|
||
|
# we need to look at all flags in the same group to figure out
|
||
|
# if we're the the default one in which case we return the flag
|
||
|
# value as default.
|
||
|
if self.is_flag and not self.is_bool_flag:
|
||
|
for param in ctx.command.params:
|
||
|
if param.name == self.name and param.default:
|
||
|
return param.flag_value
|
||
|
return None
|
||
|
return Parameter.get_default(self, ctx)
|
||
|
|
||
|
def prompt_for_value(self, ctx):
|
||
|
"""This is an alternative flow that can be activated in the full
|
||
|
value processing if a value does not exist. It will prompt the
|
||
|
user until a valid value exists and then returns the processed
|
||
|
value as result.
|
||
|
"""
|
||
|
# Calculate the default before prompting anything to be stable.
|
||
|
default = self.get_default(ctx)
|
||
|
|
||
|
# If this is a prompt for a flag we need to handle this
|
||
|
# differently.
|
||
|
if self.is_bool_flag:
|
||
|
return confirm(self.prompt, default)
|
||
|
|
||
|
return prompt(self.prompt, default=default,
|
||
|
hide_input=self.hide_input,
|
||
|
confirmation_prompt=self.confirmation_prompt,
|
||
|
value_proc=lambda x: self.process_value(ctx, x))
|
||
|
|
||
|
def resolve_envvar_value(self, ctx):
|
||
|
rv = Parameter.resolve_envvar_value(self, ctx)
|
||
|
if rv is not None:
|
||
|
return rv
|
||
|
if self.allow_from_autoenv and \
|
||
|
ctx.auto_envvar_prefix is not None:
|
||
|
envvar = '%s_%s' % (ctx.auto_envvar_prefix, self.name.upper())
|
||
|
return os.environ.get(envvar)
|
||
|
|
||
|
def value_from_envvar(self, ctx):
|
||
|
rv = self.resolve_envvar_value(ctx)
|
||
|
if rv is None:
|
||
|
return None
|
||
|
value_depth = (self.nargs != 1) + bool(self.multiple)
|
||
|
if value_depth > 0 and rv is not None:
|
||
|
rv = self.type.split_envvar_value(rv)
|
||
|
if self.multiple and self.nargs != 1:
|
||
|
rv = batch(rv, self.nargs)
|
||
|
return rv
|
||
|
|
||
|
def full_process_value(self, ctx, value):
|
||
|
if value is None and self.prompt is not None \
|
||
|
and not ctx.resilient_parsing:
|
||
|
return self.prompt_for_value(ctx)
|
||
|
return Parameter.full_process_value(self, ctx, value)
|
||
|
|
||
|
|
||
|
class Argument(Parameter):
|
||
|
"""Arguments are positional parameters to a command. They generally
|
||
|
provide fewer features than options but can have infinite ``nargs``
|
||
|
and are required by default.
|
||
|
|
||
|
All parameters are passed onwards to the parameter constructor.
|
||
|
"""
|
||
|
param_type_name = 'argument'
|
||
|
|
||
|
def __init__(self, param_decls, required=None, **attrs):
|
||
|
if required is None:
|
||
|
if attrs.get('default') is not None:
|
||
|
required = False
|
||
|
else:
|
||
|
required = attrs.get('nargs', 1) > 0
|
||
|
Parameter.__init__(self, param_decls, required=required, **attrs)
|
||
|
if self.default is not None and self.nargs < 0:
|
||
|
raise TypeError('nargs=-1 in combination with a default value '
|
||
|
'is not supported.')
|
||
|
|
||
|
@property
|
||
|
def human_readable_name(self):
|
||
|
if self.metavar is not None:
|
||
|
return self.metavar
|
||
|
return self.name.upper()
|
||
|
|
||
|
def make_metavar(self):
|
||
|
if self.metavar is not None:
|
||
|
return self.metavar
|
||
|
var = self.name.upper()
|
||
|
if not self.required:
|
||
|
var = '[%s]' % var
|
||
|
if self.nargs != 1:
|
||
|
var += '...'
|
||
|
return var
|
||
|
|
||
|
def _parse_decls(self, decls, expose_value):
|
||
|
if not decls:
|
||
|
if not expose_value:
|
||
|
return None, [], []
|
||
|
raise TypeError('Could not determine name for argument')
|
||
|
if len(decls) == 1:
|
||
|
name = arg = decls[0]
|
||
|
name = name.replace('-', '_').lower()
|
||
|
elif len(decls) == 2:
|
||
|
name, arg = decls
|
||
|
else:
|
||
|
raise TypeError('Arguments take exactly one or two '
|
||
|
'parameter declarations, got %d' % len(decls))
|
||
|
return name, [arg], []
|
||
|
|
||
|
def get_usage_pieces(self, ctx):
|
||
|
return [self.make_metavar()]
|
||
|
|
||
|
def add_to_parser(self, parser, ctx):
|
||
|
parser.add_argument(dest=self.name, nargs=self.nargs,
|
||
|
obj=self)
|
||
|
|
||
|
|
||
|
# Circular dependency between decorators and core
|
||
|
from .decorators import command, group
|