Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ Unreleased
commands. :issue:`2589`
- ``MultiCommand`` is deprecated. ``Group`` is the base class for all group
commands. :issue:`2590`
- The current parser and related classes and methods, are deprecated.
:issue:`2205`

- ``OptionParser`` and the ``parser`` module, which is a modified copy of
``optparse`` in the standard library.
- ``Context.protected_args`` is unneeded. ``Context.args`` contains any
remaining arguments while parsing.
- ``Parameter.add_to_parser`` (on both ``Argument`` and ``Option``) is
unneeded. Parsing works directly without building a separate parser.
- ``split_arg_string`` is moved from ``parser`` to ``shell_completion``.


Version 8.1.7
Expand Down
12 changes: 11 additions & 1 deletion src/click/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
from .formatting import HelpFormatter as HelpFormatter
from .formatting import wrap_text as wrap_text
from .globals import get_current_context as get_current_context
from .parser import OptionParser as OptionParser
from .termui import clear as clear
from .termui import confirm as confirm
from .termui import echo_via_pager as echo_via_pager
Expand Down Expand Up @@ -96,4 +95,15 @@ def __getattr__(name: str) -> object:
)
return _MultiCommand

if name == "OptionParser":
from .parser import _OptionParser

warnings.warn(
"'OptionParser' is deprecated and will be removed in Click 9.0. The"
" old parser is available in 'optparse'.",
DeprecationWarning,
stacklevel=2,
)
return _OptionParser

raise AttributeError(name)
52 changes: 34 additions & 18 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
from .globals import pop_context
from .globals import push_context
from .parser import _flag_needs_value
from .parser import OptionParser
from .parser import split_opt
from .parser import _OptionParser
from .parser import _split_opt
from .termui import confirm
from .termui import prompt
from .termui import style
Expand Down Expand Up @@ -224,6 +224,10 @@ class Context:
context. ``Command.show_default`` overrides this default for the
specific command.

.. versionchanged:: 8.2
The ``protected_args`` attribute is deprecated and will be removed in
Click 9.0. ``args`` will contain remaining unparsed tokens.

.. versionchanged:: 8.1
The ``show_default`` parameter is overridden by
``Command.show_default``, instead of the other way around.
Expand Down Expand Up @@ -287,7 +291,7 @@ def __init__(
#: 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: t.List[str] = []
self._protected_args: t.List[str] = []
#: the collected prefixes of the command's options.
self._opt_prefixes: t.Set[str] = set(parent._opt_prefixes) if parent else set()

Expand Down Expand Up @@ -425,6 +429,18 @@ def __init__(
self._parameter_source: t.Dict[str, ParameterSource] = {}
self._exit_stack = ExitStack()

@property
def protected_args(self) -> t.List[str]:
import warnings

warnings.warn(
"'protected_args' is deprecated and will be removed in Click 9.0."
" 'args' will contain remaining unparsed tokens.",
DeprecationWarning,
stacklevel=2,
)
return self._protected_args

def to_info_dict(self) -> t.Dict[str, t.Any]:
"""Gather information that could be useful for a tool generating
user-facing documentation. This traverses the entire CLI
Expand Down Expand Up @@ -1009,9 +1025,9 @@ def show_help(ctx: Context, param: "Parameter", value: str) -> None:
help=_("Show this message and exit."),
)

def make_parser(self, ctx: Context) -> OptionParser:
def make_parser(self, ctx: Context) -> _OptionParser:
"""Creates the underlying option parser for this command."""
parser = OptionParser(ctx)
parser = _OptionParser(ctx)
for param in self.get_params(ctx):
param.add_to_parser(parser, ctx)
return parser
Expand Down Expand Up @@ -1212,7 +1228,7 @@ def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionIte
results.extend(
CompletionItem(name, help=command.get_short_help_str())
for name, command in _complete_visible_commands(ctx, incomplete)
if name not in ctx.protected_args
if name not in ctx._protected_args
)

return results
Expand Down Expand Up @@ -1738,10 +1754,10 @@ def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]:
rest = super().parse_args(ctx, args)

if self.chain:
ctx.protected_args = rest
ctx._protected_args = rest
ctx.args = []
elif rest:
ctx.protected_args, ctx.args = rest[:1], rest[1:]
ctx._protected_args, ctx.args = rest[:1], rest[1:]

return ctx.args

Expand All @@ -1751,7 +1767,7 @@ def _process_result(value: t.Any) -> t.Any:
value = ctx.invoke(self._result_callback, value, **ctx.params)
return value

if not ctx.protected_args:
if not ctx._protected_args:
if self.invoke_without_command:
# No subcommand was invoked, so the result callback is
# invoked with the group return value for regular
Expand All @@ -1762,9 +1778,9 @@ def _process_result(value: t.Any) -> t.Any:
ctx.fail(_("Missing command."))

# Fetch args back out
args = [*ctx.protected_args, *ctx.args]
args = [*ctx._protected_args, *ctx.args]
ctx.args = []
ctx.protected_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
Expand Down Expand Up @@ -1835,7 +1851,7 @@ def resolve_command(
# resolve things like --help which now should go to the main
# place.
if cmd is None and not ctx.resilient_parsing:
if split_opt(cmd_name)[0]:
if _split_opt(cmd_name)[0]:
self.parse_args(ctx, ctx.args)
ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name))
return cmd_name if cmd else None, cmd, args[1:]
Expand Down Expand Up @@ -2193,7 +2209,7 @@ def get_default(

return value

def add_to_parser(self, parser: OptionParser, ctx: Context) -> None:
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
raise NotImplementedError()

def consume_value(
Expand Down Expand Up @@ -2582,7 +2598,7 @@ def _parse_decls(
first, second = decl.split(split_char, 1)
first = first.rstrip()
if first:
possible_names.append(split_opt(first))
possible_names.append(_split_opt(first))
opts.append(first)
second = second.lstrip()
if second:
Expand All @@ -2593,7 +2609,7 @@ def _parse_decls(
" same flag for true/false."
)
else:
possible_names.append(split_opt(decl))
possible_names.append(_split_opt(decl))
opts.append(decl)

if name is None and possible_names:
Expand All @@ -2616,7 +2632,7 @@ def _parse_decls(

return name, opts, secondary_opts

def add_to_parser(self, parser: OptionParser, ctx: Context) -> None:
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
if self.multiple:
action = "append"
elif self.count:
Expand Down Expand Up @@ -2733,7 +2749,7 @@ def _write_opts(opts: t.Sequence[str]) -> str:
elif self.is_bool_flag and self.secondary_opts:
# For boolean flags that have distinct True/False opts,
# use the opt without prefix instead of the value.
default_string = split_opt(
default_string = _split_opt(
(self.opts if self.default else self.secondary_opts)[0]
)[1]
elif self.is_bool_flag and not self.secondary_opts and not default_value:
Expand Down Expand Up @@ -2962,7 +2978,7 @@ def get_usage_pieces(self, ctx: Context) -> t.List[str]:
def get_error_hint(self, ctx: Context) -> str:
return f"'{self.make_metavar()}'"

def add_to_parser(self, parser: OptionParser, ctx: Context) -> None:
def add_to_parser(self, parser: _OptionParser, ctx: Context) -> None:
parser.add_argument(dest=self.name, nargs=self.nargs, obj=self)


Expand Down
4 changes: 2 additions & 2 deletions src/click/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from gettext import gettext as _

from ._compat import term_len
from .parser import split_opt
from .parser import _split_opt

# Can force a width. This is used by the test system
FORCED_WIDTH: t.Optional[int] = None
Expand Down Expand Up @@ -290,7 +290,7 @@ def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]:
any_prefix_is_slash = False

for opt in options:
prefix = split_opt(opt)[0]
prefix = _split_opt(opt)[0]

if prefix == "/":
any_prefix_is_slash = True
Expand Down
Loading