cli-command-parser 2024.4.21__tar.gz → 2024.5.19__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cli_command_parser-2024.4.21/lib/cli_command_parser.egg-info → cli_command_parser-2024.5.19}/PKG-INFO +4 -4
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/__version__.py +1 -1
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/command_parameters.py +24 -22
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/commands.py +8 -13
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/compat.py +3 -2
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/config.py +63 -13
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/context.py +14 -15
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/argparse_ast.py +21 -21
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/command_builder.py +15 -13
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/visitor.py +8 -6
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/core.py +20 -18
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/documentation.py +8 -8
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/error_handling.py +3 -3
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/formatting/commands.py +12 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/formatting/params.py +46 -50
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/formatting/restructured_text.py +6 -6
- cli_command_parser-2024.5.19/lib/cli_command_parser/formatting/utils.py +207 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/files.py +22 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/numeric.py +13 -28
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/patterns.py +4 -2
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/time.py +48 -19
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/utils.py +37 -4
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/metadata.py +13 -13
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/nargs.py +2 -2
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/actions.py +6 -6
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/base.py +22 -22
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/choice_map.py +31 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/groups.py +6 -6
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/option_strings.py +19 -19
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/options.py +9 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parse_tree.py +9 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parser.py +16 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/testing.py +4 -4
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/typing.py +27 -9
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/utils.py +3 -3
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19/lib/cli_command_parser.egg-info}/PKG-INFO +4 -4
- cli_command_parser-2024.5.19/pyproject.toml +73 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/readme.rst +3 -3
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/requirements-dev.txt +1 -0
- cli_command_parser-2024.4.21/lib/cli_command_parser/formatting/utils.py +0 -158
- cli_command_parser-2024.4.21/pyproject.toml +0 -21
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/LICENSE +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/MANIFEST.in +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/entry_points.txt +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/__init__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/__main__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/annotations.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/__init__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/__main__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/argparse_utils.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/cli.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/conversion/utils.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/exceptions.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/formatting/__init__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/__init__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/base.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/choices.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/inputs/exceptions.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/__init__.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/pass_thru.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/parameters/positionals.py +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser.egg-info/SOURCES.txt +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser.egg-info/dependency_links.txt +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser.egg-info/entry_points.txt +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser.egg-info/requires.txt +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser.egg-info/top_level.txt +0 -0
- {cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cli_command_parser
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.5.19
|
|
4
4
|
Summary: CLI Command Parser
|
|
5
5
|
Home-page: https://github.com/dskrypa/cli_command_parser
|
|
6
6
|
Author: Doug Skrypa
|
|
@@ -34,7 +34,7 @@ Requires-Dist: astunparse; python_version < "3.9" and extra == "conversion"
|
|
|
34
34
|
CLI Command Parser
|
|
35
35
|
##################
|
|
36
36
|
|
|
37
|
-
|downloads| |py_version| |coverage_badge| |build_status| |
|
|
37
|
+
|downloads| |py_version| |coverage_badge| |build_status| |Ruff|
|
|
38
38
|
|
|
39
39
|
.. |py_version| image:: https://img.shields.io/badge/python-3.8%20%7C%203.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20-blue
|
|
40
40
|
:target: https://pypi.org/project/cli-command-parser/
|
|
@@ -45,8 +45,8 @@ CLI Command Parser
|
|
|
45
45
|
.. |build_status| image:: https://github.com/dskrypa/cli_command_parser/actions/workflows/run-tests.yml/badge.svg
|
|
46
46
|
:target: https://github.com/dskrypa/cli_command_parser/actions/workflows/run-tests.yml
|
|
47
47
|
|
|
48
|
-
.. |
|
|
49
|
-
:target: https://
|
|
48
|
+
.. |Ruff| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
49
|
+
:target: https://docs.astral.sh/ruff/
|
|
50
50
|
|
|
51
51
|
.. |downloads| image:: https://img.shields.io/pypi/dm/cli-command-parser
|
|
52
52
|
:target: https://pypistats.org/packages/cli-command-parser
|
{cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/__version__.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
__title__ = 'cli_command_parser'
|
|
2
2
|
__description__ = 'CLI Command Parser'
|
|
3
3
|
__url__ = 'https://github.com/dskrypa/cli_command_parser'
|
|
4
|
-
__version__ = '2024.
|
|
4
|
+
__version__ = '2024.05.19'
|
|
5
5
|
__author__ = 'Doug Skrypa'
|
|
6
6
|
__author_email__ = 'dskrypa@gmail.com'
|
|
7
7
|
__license__ = 'Apache 2.0'
|
|
@@ -12,25 +12,26 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
from collections import defaultdict
|
|
14
14
|
from functools import cached_property
|
|
15
|
-
from typing import TYPE_CHECKING,
|
|
15
|
+
from typing import TYPE_CHECKING, Collection, Iterator, Optional
|
|
16
16
|
|
|
17
|
-
from .config import
|
|
18
|
-
from .exceptions import
|
|
19
|
-
from .parameters
|
|
20
|
-
from .parameters import
|
|
17
|
+
from .config import AmbiguousComboMode, CommandConfig
|
|
18
|
+
from .exceptions import AmbiguousCombo, AmbiguousShortForm, CommandDefinitionError, ParameterDefinitionError
|
|
19
|
+
from .parameters import Action, ActionFlag, ParamGroup, PassThru, SubCommand, help_action
|
|
20
|
+
from .parameters.base import BaseOption, BasePositional, ParamBase, Parameter
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from .context import Context
|
|
24
24
|
from .formatting.commands import CommandHelpFormatter
|
|
25
25
|
from .typing import CommandCls, Strings
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
OptionMap = dict[str, BaseOption]
|
|
28
|
+
ActionFlags = list[ActionFlag]
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
ActionFlags = List[ActionFlag]
|
|
30
|
+
__all__ = ['CommandParameters']
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
class CommandParameters:
|
|
34
|
+
# fmt: off
|
|
34
35
|
command: CommandCls #: The Command associated with this CommandParameters object
|
|
35
36
|
formatter: CommandHelpFormatter #: The formatter used for this Command's help text
|
|
36
37
|
command_parent: Optional[CommandCls] #: The parent Command, if any
|
|
@@ -39,13 +40,14 @@ class CommandParameters:
|
|
|
39
40
|
_pass_thru: Optional[PassThru] = None #: A PassThru Parameter, if specified
|
|
40
41
|
sub_command: Optional[SubCommand] = None #: A SubCommand Parameter, if specified
|
|
41
42
|
action_flags: ActionFlags #: List of action flags
|
|
42
|
-
split_action_flags:
|
|
43
|
-
options:
|
|
43
|
+
split_action_flags: tuple[ActionFlags, ActionFlags] #: Action flags split by before/after main
|
|
44
|
+
options: list[BaseOption] #: List of optional Parameters
|
|
44
45
|
combo_option_map: OptionMap #: Mapping of {short opt: Parameter} (no dash characters)
|
|
45
|
-
groups:
|
|
46
|
-
positionals:
|
|
47
|
-
_deferred_positionals:
|
|
46
|
+
groups: list[ParamGroup] #: List of ParamGroup objects
|
|
47
|
+
positionals: list[BasePositional] #: List of positional Parameters
|
|
48
|
+
_deferred_positionals: list[BasePositional] = () #: Positional Parameters that are deferred to sub commands
|
|
48
49
|
option_map: OptionMap #: Mapping of {--opt / -opt: Parameter}
|
|
50
|
+
# fmt: on
|
|
49
51
|
|
|
50
52
|
def __init__(
|
|
51
53
|
self,
|
|
@@ -83,7 +85,7 @@ class CommandParameters:
|
|
|
83
85
|
# endregion
|
|
84
86
|
|
|
85
87
|
@cached_property
|
|
86
|
-
def all_positionals(self) ->
|
|
88
|
+
def all_positionals(self) -> list[BasePositional]:
|
|
87
89
|
try:
|
|
88
90
|
if not self.parent.sub_command:
|
|
89
91
|
return self.parent.all_positionals + self.positionals
|
|
@@ -91,7 +93,7 @@ class CommandParameters:
|
|
|
91
93
|
pass
|
|
92
94
|
return self.positionals
|
|
93
95
|
|
|
94
|
-
def get_positionals_to_parse(self, ctx: Context) ->
|
|
96
|
+
def get_positionals_to_parse(self, ctx: Context) -> list[BasePositional]:
|
|
95
97
|
if self.all_positionals:
|
|
96
98
|
for i, param in enumerate(self.all_positionals):
|
|
97
99
|
if not ctx.num_provided(param):
|
|
@@ -170,13 +172,13 @@ class CommandParameters:
|
|
|
170
172
|
self._process_options(options)
|
|
171
173
|
self._process_groups(groups)
|
|
172
174
|
|
|
173
|
-
def _process_groups(self, groups:
|
|
175
|
+
def _process_groups(self, groups: set[ParamGroup]):
|
|
174
176
|
if self.parent:
|
|
175
177
|
self.groups = sorted((*self.parent.groups, *groups)) if groups else self.parent.groups.copy()
|
|
176
178
|
else:
|
|
177
179
|
self.groups = sorted(groups) if groups else []
|
|
178
180
|
|
|
179
|
-
def _process_positionals(self, params:
|
|
181
|
+
def _process_positionals(self, params: list[BasePositional]):
|
|
180
182
|
unfollowable = action_or_sub_cmd = split_index = None
|
|
181
183
|
if self.parent and (deferred := self.parent._deferred_positionals):
|
|
182
184
|
params = deferred + params
|
|
@@ -216,7 +218,7 @@ class CommandParameters:
|
|
|
216
218
|
|
|
217
219
|
self.positionals = params
|
|
218
220
|
|
|
219
|
-
def _process_options(self, params:
|
|
221
|
+
def _process_options(self, params: list[BaseOption]):
|
|
220
222
|
if parent := self.parent:
|
|
221
223
|
option_map = parent.option_map.copy()
|
|
222
224
|
combo_option_map = parent.combo_option_map.copy()
|
|
@@ -298,7 +300,7 @@ class CommandParameters:
|
|
|
298
300
|
# region Ambiguous Short Combo Handling
|
|
299
301
|
|
|
300
302
|
@cached_property
|
|
301
|
-
def _classified_combo_options(self) ->
|
|
303
|
+
def _classified_combo_options(self) -> tuple[OptionMap, OptionMap]:
|
|
302
304
|
multi_char_combos = {}
|
|
303
305
|
items = self.combo_option_map.items()
|
|
304
306
|
for combo, param in items:
|
|
@@ -308,7 +310,7 @@ class CommandParameters:
|
|
|
308
310
|
return {}, multi_char_combos
|
|
309
311
|
|
|
310
312
|
@cached_property
|
|
311
|
-
def _potentially_ambiguous_combo_opts(self) ->
|
|
313
|
+
def _potentially_ambiguous_combo_opts(self) -> dict[str, tuple[BaseOption, OptionMap]]:
|
|
312
314
|
return _find_ambiguous_combos(*self._classified_combo_options)
|
|
313
315
|
|
|
314
316
|
@cached_property
|
|
@@ -357,7 +359,7 @@ class CommandParameters:
|
|
|
357
359
|
|
|
358
360
|
def short_option_to_param_value_pairs(
|
|
359
361
|
self, option: str
|
|
360
|
-
) ->
|
|
362
|
+
) -> tuple[list[tuple[str, BaseOption, Optional[str]]], bool]:
|
|
361
363
|
option, eq, value = option.partition('=')
|
|
362
364
|
if eq: # An `=` was present in the string
|
|
363
365
|
# Note: if the option is not in this Command's option_map, the KeyError is handled by CommandParser
|
|
@@ -404,7 +406,7 @@ class CommandParameters:
|
|
|
404
406
|
|
|
405
407
|
def _find_ambiguous_combos(
|
|
406
408
|
single_char_combos: OptionMap, multi_char_combos: OptionMap
|
|
407
|
-
) ->
|
|
409
|
+
) -> dict[str, tuple[BaseOption, OptionMap]]:
|
|
408
410
|
ambiguous_combo_options = {}
|
|
409
411
|
for combo, param in multi_char_combos.items():
|
|
410
412
|
if singles := {c: single_char_combos[c] for c in combo if c in single_char_combos}:
|
{cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/commands.py
RENAMED
|
@@ -9,10 +9,10 @@ from __future__ import annotations
|
|
|
9
9
|
import logging
|
|
10
10
|
from abc import ABC
|
|
11
11
|
from contextlib import ExitStack
|
|
12
|
-
from typing import TYPE_CHECKING,
|
|
12
|
+
from typing import TYPE_CHECKING, Optional, Sequence, Type, overload
|
|
13
13
|
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
14
|
+
from .context import ActionPhase, Context, get_or_create_context
|
|
15
|
+
from .core import CommandMeta, get_params, get_top_level_commands
|
|
16
16
|
from .exceptions import ParamConflict
|
|
17
17
|
from .parser import parse_args_and_get_next_cmd
|
|
18
18
|
from .utils import maybe_await
|
|
@@ -29,9 +29,6 @@ Argv = Sequence[str]
|
|
|
29
29
|
class Command(ABC, metaclass=CommandMeta):
|
|
30
30
|
"""The main class that other Commands should extend."""
|
|
31
31
|
|
|
32
|
-
# TODO: Make the distinction between help/description clearer, or merge them?
|
|
33
|
-
# TODO: Pull help text from docstring for subcommands if not specified as help=?
|
|
34
|
-
|
|
35
32
|
#: The parsing Context used for this Command. Provided here for convenience - this reference to it is not used by
|
|
36
33
|
#: any CLI Command Parser internals, so it is safe for subclasses to redefine / overwrite it.
|
|
37
34
|
ctx: Context
|
|
@@ -54,12 +51,12 @@ class Command(ABC, metaclass=CommandMeta):
|
|
|
54
51
|
@classmethod
|
|
55
52
|
@overload
|
|
56
53
|
def parse_and_run(cls: Type[CommandObj], argv: Argv = None, **kwargs) -> Optional[CommandObj]:
|
|
57
|
-
|
|
54
|
+
# These overloads indicate that an instance of the same type or another may be returned
|
|
55
|
+
...
|
|
58
56
|
|
|
59
57
|
@classmethod
|
|
60
58
|
@overload
|
|
61
|
-
def parse_and_run(cls, argv: Argv = None, **kwargs) -> Optional[CommandObj]:
|
|
62
|
-
...
|
|
59
|
+
def parse_and_run(cls, argv: Argv = None, **kwargs) -> Optional[CommandObj]: ...
|
|
63
60
|
|
|
64
61
|
@classmethod
|
|
65
62
|
def parse_and_run(cls, argv=None, **kwargs):
|
|
@@ -94,13 +91,11 @@ class Command(ABC, metaclass=CommandMeta):
|
|
|
94
91
|
|
|
95
92
|
@classmethod
|
|
96
93
|
@overload
|
|
97
|
-
def parse(cls: Type[CommandObj], argv: Argv = None) -> CommandObj:
|
|
98
|
-
...
|
|
94
|
+
def parse(cls: Type[CommandObj], argv: Argv = None) -> CommandObj: ...
|
|
99
95
|
|
|
100
96
|
@classmethod
|
|
101
97
|
@overload
|
|
102
|
-
def parse(cls, argv: Argv = None) -> CommandObj:
|
|
103
|
-
...
|
|
98
|
+
def parse(cls, argv: Argv = None) -> CommandObj: ...
|
|
104
99
|
|
|
105
100
|
@classmethod
|
|
106
101
|
def parse(cls, argv=None):
|
{cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/compat.py
RENAMED
|
@@ -8,8 +8,9 @@ The :class:`WCTextWrapper` in this module extends the stdlib :class:`python:text
|
|
|
8
8
|
characters.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
11
13
|
from textwrap import TextWrapper
|
|
12
|
-
from typing import List
|
|
13
14
|
|
|
14
15
|
from .utils import wcswidth
|
|
15
16
|
|
|
@@ -24,7 +25,7 @@ class WCTextWrapper(TextWrapper):
|
|
|
24
25
|
optional ``wcwidth`` dependency is available). Minimal formatting changes are applied. No logic has been changed.
|
|
25
26
|
"""
|
|
26
27
|
|
|
27
|
-
def _wrap_chunks(self, chunks:
|
|
28
|
+
def _wrap_chunks(self, chunks: list[str]) -> list[str]:
|
|
28
29
|
"""
|
|
29
30
|
_wrap_chunks(chunks : [string]) -> [string]
|
|
30
31
|
|
{cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/config.py
RENAMED
|
@@ -8,8 +8,10 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from collections import ChainMap
|
|
10
10
|
from enum import Enum
|
|
11
|
-
from
|
|
11
|
+
from string import whitespace
|
|
12
|
+
from typing import TYPE_CHECKING, Any, Callable, Generic, Optional, Sequence, Type, TypeVar, Union, overload
|
|
12
13
|
|
|
14
|
+
from .exceptions import CommandDefinitionError
|
|
13
15
|
from .utils import FixedFlag, MissingMixin, _NotSet, positive_int
|
|
14
16
|
|
|
15
17
|
if TYPE_CHECKING:
|
|
@@ -17,7 +19,7 @@ if TYPE_CHECKING:
|
|
|
17
19
|
from .error_handling import ErrorHandler
|
|
18
20
|
from .formatting.commands import CommandHelpFormatter
|
|
19
21
|
from .formatting.params import ParamHelpFormatter
|
|
20
|
-
from .typing import Bool,
|
|
22
|
+
from .typing import Bool, CommandType, ParamOrGroup
|
|
21
23
|
|
|
22
24
|
__all__ = [
|
|
23
25
|
'CommandConfig',
|
|
@@ -49,11 +51,13 @@ class ShowDefaults(FixedFlag):
|
|
|
49
51
|
is equivalent to ``ShowDefaults.MISSING | ShowDefaults.NEVER``, which will result in no default values being shown.
|
|
50
52
|
"""
|
|
51
53
|
|
|
54
|
+
# fmt: off
|
|
52
55
|
NEVER = 1 #: Never include the default value in help text
|
|
53
56
|
MISSING = 2 #: Only include the default value if ``default:`` is not already present
|
|
54
57
|
TRUTHY = 4 #: Only include the default value if it is treated as True in a boolean context
|
|
55
58
|
NON_EMPTY = 8 #: Only include the default value if it is not ``None`` or an empty container
|
|
56
59
|
ANY = 16 #: Any default value, regardless of truthiness, will be included
|
|
60
|
+
# fmt: on
|
|
57
61
|
|
|
58
62
|
@classmethod
|
|
59
63
|
def _missing_(cls, value: Union[str, int]) -> ShowDefaults:
|
|
@@ -104,6 +108,7 @@ class OptionNameMode(FixedFlag):
|
|
|
104
108
|
- ``'underscore'`` or ``'dash'`` or ``'both'`` or ``'both_underscore'`` or ``'both_dash'`` or ``'none'``
|
|
105
109
|
"""
|
|
106
110
|
|
|
111
|
+
# fmt: off
|
|
107
112
|
UNDERSCORE = 1
|
|
108
113
|
DASH = 2
|
|
109
114
|
BOTH = 3 # = 1|2
|
|
@@ -111,6 +116,7 @@ class OptionNameMode(FixedFlag):
|
|
|
111
116
|
BOTH_UNDERSCORE = 15 # & 8 -> show only underscore version
|
|
112
117
|
BOTH_DASH = 23 # & 16 -> show only dash version
|
|
113
118
|
NONE = 32
|
|
119
|
+
# fmt: on
|
|
114
120
|
|
|
115
121
|
@classmethod
|
|
116
122
|
def _missing_(cls, value: Union[str, int, None]) -> OptionNameMode:
|
|
@@ -153,9 +159,11 @@ class SubcommandAliasHelpMode(MissingMixin, Enum):
|
|
|
153
159
|
``Alias of: <first choice/alias value>``.
|
|
154
160
|
"""
|
|
155
161
|
|
|
162
|
+
# fmt: off
|
|
156
163
|
REPEAT = 'repeat' # Repeat the description as if it was a separate subcommand
|
|
157
164
|
COMBINE = 'combine' # Combine aliases onto a single line
|
|
158
165
|
ALIAS = 'alias' # Indicate the subcommand that it is an alias for; do not repeat the description
|
|
166
|
+
# fmt: on
|
|
159
167
|
|
|
160
168
|
|
|
161
169
|
CmdAliasMode = Union[SubcommandAliasHelpMode, str]
|
|
@@ -176,9 +184,11 @@ class AmbiguousComboMode(MissingMixin, Enum):
|
|
|
176
184
|
input.
|
|
177
185
|
"""
|
|
178
186
|
|
|
187
|
+
# fmt: off
|
|
179
188
|
IGNORE = 'ignore' # Ignore potentially ambiguous combinations of short options entirely
|
|
180
189
|
PERMISSIVE = 'permissive' # Allow multi-char short options that overlap with a single char one for exact matches
|
|
181
190
|
STRICT = 'strict' # Reject multi-char short options that overlap with a single char one before parsing
|
|
191
|
+
# fmt: on
|
|
182
192
|
|
|
183
193
|
|
|
184
194
|
class AllowLeadingDash(Enum):
|
|
@@ -193,9 +203,11 @@ class AllowLeadingDash(Enum):
|
|
|
193
203
|
:NEVER: Never allow values with a leading dash.
|
|
194
204
|
"""
|
|
195
205
|
|
|
206
|
+
# fmt: off
|
|
196
207
|
NUMERIC = 'numeric' # Allow a leading dash when the value is numeric
|
|
197
208
|
ALWAYS = 'always' # Always allow a leading dash
|
|
198
209
|
NEVER = 'never' # Never allow a leading dash
|
|
210
|
+
# fmt: on
|
|
199
211
|
|
|
200
212
|
@classmethod
|
|
201
213
|
def _missing_(cls, value):
|
|
@@ -218,6 +230,13 @@ class AllowLeadingDash(Enum):
|
|
|
218
230
|
|
|
219
231
|
|
|
220
232
|
class ConfigItem(Generic[CV, DV]):
|
|
233
|
+
"""
|
|
234
|
+
A single configurable setting in the :class:`CommandConfig`.
|
|
235
|
+
|
|
236
|
+
:param default: Default config value to use if no explicit value is provided
|
|
237
|
+
:param type: A class or other callable that will be called to validate/normalize provided values
|
|
238
|
+
"""
|
|
239
|
+
|
|
221
240
|
__slots__ = ('default', 'type', 'name')
|
|
222
241
|
|
|
223
242
|
def __init__(self, default: DV, type: Callable[..., CV] = None): # noqa
|
|
@@ -229,17 +248,16 @@ class ConfigItem(Generic[CV, DV]):
|
|
|
229
248
|
owner.FIELDS.add(name)
|
|
230
249
|
|
|
231
250
|
@overload
|
|
232
|
-
def __get__(self, instance: None, owner: Type[CommandConfig]) -> ConfigItem[CV, DV]:
|
|
233
|
-
...
|
|
251
|
+
def __get__(self, instance: None, owner: Type[CommandConfig]) -> ConfigItem[CV, DV]: ...
|
|
234
252
|
|
|
235
253
|
@overload
|
|
236
|
-
def __get__(self, instance: CommandConfig, owner: Type[CommandConfig]) -> ConfigValue:
|
|
237
|
-
...
|
|
254
|
+
def __get__(self, instance: CommandConfig, owner: Type[CommandConfig]) -> ConfigValue: ...
|
|
238
255
|
|
|
239
256
|
def __get__(self, instance, owner):
|
|
240
|
-
|
|
257
|
+
try:
|
|
258
|
+
return instance._data.get(self.name, self.default)
|
|
259
|
+
except AttributeError: # instance is None
|
|
241
260
|
return self
|
|
242
|
-
return instance._data.get(self.name, self.default)
|
|
243
261
|
|
|
244
262
|
def __set__(self, instance: CommandConfig, value: ConfigValue):
|
|
245
263
|
if instance._read_only:
|
|
@@ -261,6 +279,8 @@ class ConfigItem(Generic[CV, DV]):
|
|
|
261
279
|
|
|
262
280
|
|
|
263
281
|
class DynamicConfigItem(ConfigItem):
|
|
282
|
+
# A ConfigItem with a setter :paramref:`.ConfigItem.type` defined as a method in :class:`CommandConfig`.
|
|
283
|
+
|
|
264
284
|
__slots__ = ('__doc__',)
|
|
265
285
|
|
|
266
286
|
def __init__(self, default: DV, type: Callable[..., CV]): # noqa
|
|
@@ -372,6 +392,35 @@ class CommandConfig:
|
|
|
372
392
|
#: Whether there should be a visual indicator in help text for the parameters that are members of a given group
|
|
373
393
|
show_group_tree: Bool = ConfigItem(False, bool)
|
|
374
394
|
|
|
395
|
+
@config_item(('\u00a6 ', '\u2551 ', '\u2502 '))
|
|
396
|
+
def group_tree_spacers(self, value: tuple[str, str, str] | Sequence[str]) -> tuple[str, str, str]:
|
|
397
|
+
"""
|
|
398
|
+
The spacer characters to use at the beginning of each line when :attr:`.show_group_tree` is True.
|
|
399
|
+
|
|
400
|
+
The default spacers:
|
|
401
|
+
|
|
402
|
+
+--------------------+-----------+------------------------------+
|
|
403
|
+
| Parameter Type | Character | Character Name |
|
|
404
|
+
+====================+===========+==============================+
|
|
405
|
+
| Mutually Exclusive | \u00a6 | BROKEN BAR |
|
|
406
|
+
+--------------------+-----------+------------------------------+
|
|
407
|
+
| Mutually dependent | \u2551 | BOX DRAWINGS DOUBLE VERTICAL |
|
|
408
|
+
+--------------------+-----------+------------------------------+
|
|
409
|
+
| Other | \u2502 | BOX DRAWINGS LIGHT VERTICAL |
|
|
410
|
+
+--------------------+-----------+------------------------------+
|
|
411
|
+
|
|
412
|
+
:param value: A 3-tuple (or other sequence with 3 items) of spacer strings to be used for
|
|
413
|
+
(mutually exclusive, mutually dependent, other) group members, respectively.
|
|
414
|
+
:return: The validated and normalized value (or the default value if this property is accessed without
|
|
415
|
+
providing explicit values)
|
|
416
|
+
"""
|
|
417
|
+
# Note: extra spaces in the docstring table are intentional - the escape sequences each collapse to one char
|
|
418
|
+
if isinstance(value, Sequence) and len(value) == 3 and all(isinstance(v, str) for v in value):
|
|
419
|
+
return tuple(f'{v} ' if v and v[-1] not in whitespace else v for v in value) # noqa
|
|
420
|
+
raise CommandDefinitionError(
|
|
421
|
+
f'Invalid group_tree_spacers={value!r} - expected a 3-tuple of 2-character strings'
|
|
422
|
+
)
|
|
423
|
+
|
|
375
424
|
#: Whether mutually exclusive / dependent groups should include that fact in their descriptions
|
|
376
425
|
show_group_type: Bool = ConfigItem(True, bool)
|
|
377
426
|
|
|
@@ -386,16 +435,17 @@ class CommandConfig:
|
|
|
386
435
|
#: they were successfully detected
|
|
387
436
|
extended_epilog: Bool = ConfigItem(True, bool)
|
|
388
437
|
|
|
389
|
-
#: Width (in characters) for the usage column in help text
|
|
438
|
+
#: Width (in characters) for the usage column in help text, after which the parameter descriptions begin.
|
|
390
439
|
usage_column_width: int = ConfigItem(30, int)
|
|
391
440
|
|
|
392
|
-
#:
|
|
393
|
-
|
|
441
|
+
#: Whether the :attr:`.usage_column_width` should be enforced for parameters with usage text parts that exceed it.
|
|
442
|
+
#: By default, that setting only defines where the parameter descriptions begin.
|
|
443
|
+
strict_usage_column_width: bool = ConfigItem(False, bool)
|
|
394
444
|
|
|
395
445
|
@config_item(False)
|
|
396
446
|
def wrap_usage_str(self, value: Any) -> Union[int, bool]:
|
|
397
447
|
"""
|
|
398
|
-
Wrap the basic usage
|
|
448
|
+
Wrap the basic usage line after the specified number of characters, or automatically based on terminal size
|
|
399
449
|
if ``True`` is specified instead.
|
|
400
450
|
"""
|
|
401
451
|
if value is True or value is False:
|
|
@@ -432,7 +482,7 @@ class CommandConfig:
|
|
|
432
482
|
settings = ', '.join(f'{k}={v!r}' for k, v in self.as_dict(False).items())
|
|
433
483
|
return f'<{self.__class__.__name__}[depth={len(self._data.maps)}]({settings})>'
|
|
434
484
|
|
|
435
|
-
def as_dict(self, full: Bool = True) ->
|
|
485
|
+
def as_dict(self, full: Bool = True) -> dict[str, Any]:
|
|
436
486
|
"""Return a dict representing the configured options."""
|
|
437
487
|
if full:
|
|
438
488
|
return {key: getattr(self, key) for key in self.FIELDS}
|
{cli_command_parser-2024.4.21 → cli_command_parser-2024.5.19}/lib/cli_command_parser/context.py
RENAMED
|
@@ -13,20 +13,19 @@ from contextlib import AbstractContextManager
|
|
|
13
13
|
from contextvars import ContextVar
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from functools import cached_property
|
|
16
|
-
from inspect import
|
|
17
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
|
18
|
-
from typing import Dict, Tuple, List
|
|
16
|
+
from inspect import Parameter as _Parameter, Signature
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Callable, Collection, Iterator, Optional, Sequence, Union, cast
|
|
19
18
|
|
|
20
|
-
from .config import
|
|
19
|
+
from .config import DEFAULT_CONFIG, CommandConfig
|
|
21
20
|
from .error_handling import ErrorHandler, NullErrorHandler, extended_error_handler
|
|
22
21
|
from .exceptions import NoActiveContext
|
|
23
|
-
from .utils import
|
|
22
|
+
from .utils import Terminal, _NotSet
|
|
24
23
|
|
|
25
24
|
if TYPE_CHECKING:
|
|
26
25
|
from .command_parameters import CommandParameters
|
|
27
26
|
from .commands import Command
|
|
28
|
-
from .parameters import
|
|
29
|
-
from .typing import Bool,
|
|
27
|
+
from .parameters import ActionFlag, Option, Parameter
|
|
28
|
+
from .typing import AnyConfig, Bool, CommandObj, CommandType, OptStr, ParamOrGroup, PathLike, StrSeq # noqa
|
|
30
29
|
|
|
31
30
|
__all__ = ['Context', 'ctx', 'get_current_context', 'get_or_create_context', 'get_context', 'get_parsed', 'get_raw_arg']
|
|
32
31
|
|
|
@@ -49,7 +48,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
49
48
|
allow_argv_prog: Bool = True
|
|
50
49
|
_command_obj: CommandObj = None
|
|
51
50
|
_terminal_width: Optional[int]
|
|
52
|
-
_provided:
|
|
51
|
+
_provided: dict[ParamOrGroup, int]
|
|
53
52
|
|
|
54
53
|
def __init__(
|
|
55
54
|
self,
|
|
@@ -158,7 +157,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
158
157
|
recursive: Bool = True,
|
|
159
158
|
default: Any = None,
|
|
160
159
|
include_defaults: Bool = True,
|
|
161
|
-
) ->
|
|
160
|
+
) -> dict[str, Any]:
|
|
162
161
|
"""
|
|
163
162
|
Returns all of the parsed arguments as a dictionary.
|
|
164
163
|
|
|
@@ -247,7 +246,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
247
246
|
"""Not intended to be called by users. Used by Parameters during parsing to handle nargs."""
|
|
248
247
|
return self._provided[param]
|
|
249
248
|
|
|
250
|
-
def get_missing(self) ->
|
|
249
|
+
def get_missing(self) -> list[Parameter]:
|
|
251
250
|
"""Not intended to be called by users. Used during parsing to determine if any Parameters are missing."""
|
|
252
251
|
return [p for p in self.params.required_check_params() if not self._provided[p]]
|
|
253
252
|
|
|
@@ -260,7 +259,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
260
259
|
# region Actions
|
|
261
260
|
|
|
262
261
|
@cached_property
|
|
263
|
-
def _parsed_action_flags(self) ->
|
|
262
|
+
def _parsed_action_flags(self) -> tuple[int, list[ActionFlag], list[ActionFlag]]:
|
|
264
263
|
"""
|
|
265
264
|
Not intended to be accessed by users. Returns a tuple containing the total number of action flags provided, the
|
|
266
265
|
action flags to run before main, and the action flags to run after main.
|
|
@@ -281,13 +280,13 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
281
280
|
return self._parsed_action_flags[0]
|
|
282
281
|
|
|
283
282
|
@cached_property
|
|
284
|
-
def all_action_flags(self) ->
|
|
283
|
+
def all_action_flags(self) -> list[ActionFlag]:
|
|
285
284
|
"""Not intended to be accessed by users. Returns all parsed action flags."""
|
|
286
285
|
_, before_main, after_main = self._parsed_action_flags
|
|
287
286
|
return before_main + after_main
|
|
288
287
|
|
|
289
288
|
@cached_property
|
|
290
|
-
def categorized_action_flags(self) ->
|
|
289
|
+
def categorized_action_flags(self) -> dict[ActionPhase, Sequence[ActionFlag]]:
|
|
291
290
|
"""
|
|
292
291
|
Not intended to be accessed by users. Returns a dict of parsed action flags, categorized by the
|
|
293
292
|
:class:`ActionPhase` during which they will run.
|
|
@@ -321,7 +320,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
321
320
|
|
|
322
321
|
|
|
323
322
|
def _normalize_config(
|
|
324
|
-
config: AnyConfig, kwargs:
|
|
323
|
+
config: AnyConfig, kwargs: dict[str, Any], parent: Context | None, command: CommandType | None
|
|
325
324
|
) -> CommandConfig:
|
|
326
325
|
if config is not None:
|
|
327
326
|
if kwargs:
|
|
@@ -469,7 +468,7 @@ def get_context(command: Command) -> Context:
|
|
|
469
468
|
|
|
470
469
|
def get_parsed(
|
|
471
470
|
command: Command, to_call: Callable = None, default: Any = None, include_defaults: Bool = True
|
|
472
|
-
) ->
|
|
471
|
+
) -> dict[str, Any]:
|
|
473
472
|
"""
|
|
474
473
|
Provides a way to obtain all of the arguments that were parsed for the given Command as a dictionary.
|
|
475
474
|
|