cli-command-parser 2023.4.15__tar.gz → 2023.4.16__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-2023.4.15 → cli_command_parser-2023.4.16}/PKG-INFO +11 -1
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/__version__.py +1 -1
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/command_parameters.py +21 -17
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/commands.py +2 -2
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/context.py +7 -24
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/metadata.py +94 -19
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/base.py +5 -5
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/choice_map.py +24 -17
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/options.py +3 -3
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parser.py +26 -49
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser.egg-info/PKG-INFO +11 -1
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser.egg-info/requires.txt +3 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/readme.rst +10 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/setup.cfg +2 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/LICENSE +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/MANIFEST.in +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/__init__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/__main__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/actions.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/annotations.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/compat.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/config.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/__init__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/__main__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/argparse_ast.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/argparse_utils.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/command_builder.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/utils.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/conversion/visitor.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/core.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/documentation.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/error_handling.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/exceptions.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/formatting/__init__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/formatting/commands.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/formatting/params.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/formatting/restructured_text.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/formatting/utils.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/__init__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/base.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/choices.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/exceptions.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/files.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/numeric.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/time.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/utils.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/nargs.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/__init__.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/groups.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/option_strings.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/pass_thru.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parameters/positionals.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parse_tree.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/testing.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/typing.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/utils.py +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser.egg-info/SOURCES.txt +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser.egg-info/dependency_links.txt +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser.egg-info/top_level.txt +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/pyproject.toml +0 -0
- {cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/requirements-dev.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cli_command_parser
|
|
3
|
-
Version: 2023.4.
|
|
3
|
+
Version: 2023.4.16
|
|
4
4
|
Summary: CLI Command Parser
|
|
5
5
|
Home-page: https://github.com/dskrypa/cli_command_parser
|
|
6
6
|
Author: Doug Skrypa
|
|
@@ -110,6 +110,16 @@ with optional dependencies::
|
|
|
110
110
|
|
|
111
111
|
$ pip install -U cli-command-parser[wcwidth]
|
|
112
112
|
|
|
113
|
+
|
|
114
|
+
Python Version Compatibility
|
|
115
|
+
============================
|
|
116
|
+
|
|
117
|
+
Python versions 3.7 and above are currently supported. CLI Command Parser will no longer support 3.7 after 2023-04-30,
|
|
118
|
+
ahead of the `official end of support for 3.7 on 2023-06-27 <https://devguide.python.org/versions/>`__.
|
|
119
|
+
|
|
120
|
+
When using 3.7 or 3.8, some additional packages that backport functionality that was added in later Python versions
|
|
121
|
+
are required for compatibility.
|
|
122
|
+
|
|
113
123
|
To use the argparse to cli-command-parser conversion script with Python 3.7 or 3.8, there is a dependency on
|
|
114
124
|
`astunparse <https://astunparse.readthedocs.io>`__. If you are using Python 3.9 or above, then ``astunparse`` is not
|
|
115
125
|
necessary because the relevant code was added to the stdlib ``ast`` module. If you're unsure, you can install
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/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__ = '2023.04.
|
|
4
|
+
__version__ = '2023.04.16'
|
|
5
5
|
__author__ = 'Doug Skrypa'
|
|
6
6
|
__author_email__ = 'dskrypa@gmail.com'
|
|
7
7
|
__license__ = 'Apache 2.0'
|
|
@@ -50,6 +50,7 @@ class CommandParameters:
|
|
|
50
50
|
combo_option_map: OptionMap #: Mapping of {short opt: Parameter} (no dash characters)
|
|
51
51
|
groups: List[ParamGroup] #: List of ParamGroup objects
|
|
52
52
|
positionals: List[BasePositional] #: List of positional Parameters
|
|
53
|
+
_deferred_positionals: List[BasePositional] = () #: Positional Parameters that are deferred to sub commands
|
|
53
54
|
option_map: OptionMap #: Mapping of {--opt / -opt: Parameter}
|
|
54
55
|
|
|
55
56
|
def __init__(self, command: CommandCls, command_parent: Optional[CommandCls], config: CommandConfig):
|
|
@@ -169,6 +170,7 @@ class CommandParameters:
|
|
|
169
170
|
options.append(param)
|
|
170
171
|
elif isinstance(param, ParamGroup):
|
|
171
172
|
# Groups will only be discovered here when defined with `as` - ex: `with ParamGroup(...) as foo:`
|
|
173
|
+
# Group members will always be discovered at the top level since context managers share the outer scope
|
|
172
174
|
groups.add(param)
|
|
173
175
|
elif isinstance(param, PassThru):
|
|
174
176
|
if self.pass_thru:
|
|
@@ -200,36 +202,37 @@ class CommandParameters:
|
|
|
200
202
|
self.groups = sorted(groups)
|
|
201
203
|
|
|
202
204
|
def _process_positionals(self, params: List[BasePositional]):
|
|
203
|
-
var_nargs_param = None
|
|
204
|
-
for param in params:
|
|
205
|
-
if
|
|
206
|
-
raise CommandDefinitionError(
|
|
207
|
-
f'Positional param={param!r} may not follow the sub command {self.sub_command} - re-order the'
|
|
208
|
-
' positionals, move it into the sub command(s), or convert it to an optional parameter'
|
|
209
|
-
)
|
|
210
|
-
elif var_nargs_param:
|
|
205
|
+
var_nargs_param = action_or_sub_cmd = split_index = None
|
|
206
|
+
for i, param in enumerate(params):
|
|
207
|
+
if var_nargs_param:
|
|
211
208
|
raise CommandDefinitionError(
|
|
212
209
|
f'Additional Positional parameters cannot follow {var_nargs_param} because it accepts'
|
|
213
210
|
f' a variable number of arguments with no specific choices defined - param={param!r} is invalid'
|
|
214
211
|
)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
if self.action: # self.sub_command being already defined is handled above
|
|
212
|
+
elif isinstance(param, (SubCommand, Action)):
|
|
213
|
+
if action_or_sub_cmd:
|
|
218
214
|
raise CommandDefinitionError(
|
|
219
215
|
f'Only 1 Action xor SubCommand is allowed in a given Command - {self.command.__name__} cannot'
|
|
220
|
-
f' contain both {
|
|
216
|
+
f' contain both {action_or_sub_cmd} and {param}'
|
|
221
217
|
)
|
|
222
218
|
elif isinstance(param, SubCommand):
|
|
223
|
-
self.sub_command = param
|
|
219
|
+
self.sub_command = action_or_sub_cmd = param
|
|
220
|
+
split_index = i + 1
|
|
224
221
|
else:
|
|
225
|
-
self.action = param
|
|
222
|
+
self.action = action_or_sub_cmd = param
|
|
226
223
|
if not param.has_choices:
|
|
227
224
|
raise CommandDefinitionError(f'No choices were registered for {self.action}')
|
|
228
|
-
|
|
229
|
-
if param.nargs.variable and not param.has_choices:
|
|
225
|
+
elif param.nargs.variable and not param.has_choices:
|
|
230
226
|
var_nargs_param = param
|
|
231
227
|
|
|
232
|
-
|
|
228
|
+
if split_index:
|
|
229
|
+
params, self._deferred_positionals = params[:split_index], params[split_index:]
|
|
230
|
+
|
|
231
|
+
parent = self.parent
|
|
232
|
+
if parent and parent._deferred_positionals:
|
|
233
|
+
self.positionals = parent._deferred_positionals + params
|
|
234
|
+
else:
|
|
235
|
+
self.positionals = params
|
|
233
236
|
|
|
234
237
|
def _process_options(self, params: Collection[BaseOption]):
|
|
235
238
|
parent = self.parent
|
|
@@ -459,6 +462,7 @@ class CommandParameters:
|
|
|
459
462
|
raise exc
|
|
460
463
|
|
|
461
464
|
def try_env_params(self, ctx: Context) -> Iterator[Option]:
|
|
465
|
+
"""Yields Option parameters that have an environment variable configured, and did not have any CLI values."""
|
|
462
466
|
for param in self.options:
|
|
463
467
|
try:
|
|
464
468
|
param.env_var # noqa
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/commands.py
RENAMED
|
@@ -108,11 +108,11 @@ class Command(ABC, metaclass=CommandMeta):
|
|
|
108
108
|
cmd_cls = cls
|
|
109
109
|
with ExitStack() as stack:
|
|
110
110
|
stack.enter_context(ctx)
|
|
111
|
-
sub_cmd = CommandParser.
|
|
111
|
+
sub_cmd = CommandParser.parse_args_and_get_next_cmd(ctx)
|
|
112
112
|
while sub_cmd:
|
|
113
113
|
cmd_cls = sub_cmd
|
|
114
114
|
ctx = stack.enter_context(ctx._sub_context(cmd_cls))
|
|
115
|
-
sub_cmd = CommandParser.
|
|
115
|
+
sub_cmd = CommandParser.parse_args_and_get_next_cmd(ctx)
|
|
116
116
|
|
|
117
117
|
return cmd_cls()
|
|
118
118
|
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/context.py
RENAMED
|
@@ -32,31 +32,12 @@ if TYPE_CHECKING:
|
|
|
32
32
|
from .parameters import Parameter, ActionFlag
|
|
33
33
|
from .typing import Bool, ParamOrGroup, CommandType, AnyConfig, OptStr, PathLike
|
|
34
34
|
|
|
35
|
-
__all__ = [
|
|
36
|
-
'Context',
|
|
37
|
-
'ctx',
|
|
38
|
-
'get_current_context',
|
|
39
|
-
'get_or_create_context',
|
|
40
|
-
'get_context',
|
|
41
|
-
'get_parsed',
|
|
42
|
-
'get_raw_arg',
|
|
43
|
-
'ParseState',
|
|
44
|
-
]
|
|
35
|
+
__all__ = ['Context', 'ctx', 'get_current_context', 'get_or_create_context', 'get_context', 'get_parsed', 'get_raw_arg']
|
|
45
36
|
|
|
46
37
|
_context_stack = ContextVar('cli_command_parser.context.stack', default=[])
|
|
47
38
|
_TERMINAL = Terminal()
|
|
48
39
|
|
|
49
40
|
|
|
50
|
-
class ParseState(Enum):
|
|
51
|
-
INITIAL = 1
|
|
52
|
-
COMPLETE = 2
|
|
53
|
-
FAILED = 3
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def done(self) -> bool:
|
|
57
|
-
return self._value_ > 1
|
|
58
|
-
|
|
59
|
-
|
|
60
41
|
class Context(AbstractContextManager): # Extending AbstractContextManager to make PyCharm's type checker happy
|
|
61
42
|
"""
|
|
62
43
|
The parsing context.
|
|
@@ -69,6 +50,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
69
50
|
prog: OptStr = None
|
|
70
51
|
_terminal_width: Optional[int]
|
|
71
52
|
allow_argv_prog: Bool = True
|
|
53
|
+
_provided: Dict[ParamOrGroup, int]
|
|
72
54
|
|
|
73
55
|
def __init__(
|
|
74
56
|
self,
|
|
@@ -82,7 +64,6 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
82
64
|
):
|
|
83
65
|
self.command = command
|
|
84
66
|
self.parent = parent
|
|
85
|
-
self.state = ParseState.INITIAL
|
|
86
67
|
self.config = _normalize_config(config, kwargs, parent, command)
|
|
87
68
|
if parent is not None:
|
|
88
69
|
self._set_argv(parent.prog, argv)
|
|
@@ -128,8 +109,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
128
109
|
return self.__class__(argv, command, parent=self, **kwargs)
|
|
129
110
|
|
|
130
111
|
def __repr__(self) -> str:
|
|
131
|
-
|
|
132
|
-
return f'<{self.__class__.__name__}[state={self.state}, command={cmd_name}]>'
|
|
112
|
+
return f'<{self.__class__.__name__}[command={getattr(self.command, "__name__", None)}]>'
|
|
133
113
|
|
|
134
114
|
def __enter__(self) -> Context:
|
|
135
115
|
_context_stack.get().append(self)
|
|
@@ -220,7 +200,7 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
220
200
|
try:
|
|
221
201
|
return self._parsed[param]
|
|
222
202
|
except KeyError:
|
|
223
|
-
self._parsed[param] = value = param._init_value_factory(
|
|
203
|
+
self._parsed[param] = value = param._init_value_factory()
|
|
224
204
|
return value
|
|
225
205
|
|
|
226
206
|
def set_parsed_value(self, param: Parameter, value: Any):
|
|
@@ -237,6 +217,9 @@ class Context(AbstractContextManager): # Extending AbstractContextManager to ma
|
|
|
237
217
|
"""Not intended to be called by users. Used by Parameters during parsing to handle nargs."""
|
|
238
218
|
return self._provided[param]
|
|
239
219
|
|
|
220
|
+
def get_missing(self) -> List[Parameter]:
|
|
221
|
+
return [p for p in self.params.required_check_params() if self._provided[p] == 0]
|
|
222
|
+
|
|
240
223
|
# endregion
|
|
241
224
|
|
|
242
225
|
# region Actions
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/metadata.py
RENAMED
|
@@ -7,12 +7,20 @@ Program metadata introspection for use in usage, help text, and documentation.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
from collections import defaultdict
|
|
10
11
|
from inspect import getmodule
|
|
11
12
|
from pathlib import Path
|
|
13
|
+
from sys import modules
|
|
12
14
|
from textwrap import dedent
|
|
13
15
|
from typing import TYPE_CHECKING, Any, Type, Optional, Union, Tuple, Dict
|
|
14
16
|
from urllib.parse import urlparse
|
|
15
17
|
|
|
18
|
+
try:
|
|
19
|
+
from importlib.metadata import entry_points, EntryPoint
|
|
20
|
+
except ImportError: # Python 3.7
|
|
21
|
+
from importlib_metadata import entry_points, EntryPoint
|
|
22
|
+
|
|
23
|
+
from .compat import cached_property
|
|
16
24
|
from .context import ctx, NoActiveContext
|
|
17
25
|
|
|
18
26
|
if TYPE_CHECKING:
|
|
@@ -61,7 +69,7 @@ class ProgramMetadata:
|
|
|
61
69
|
module: str = Metadata(None)
|
|
62
70
|
command: str = Metadata(None)
|
|
63
71
|
prog: str = Metadata(None)
|
|
64
|
-
|
|
72
|
+
prog_src: str = Metadata(None)
|
|
65
73
|
url: str = Metadata(None)
|
|
66
74
|
docs_url: str = Metadata(None)
|
|
67
75
|
email: str = Metadata(None)
|
|
@@ -111,7 +119,7 @@ class ProgramMetadata:
|
|
|
111
119
|
else:
|
|
112
120
|
doc = doc_str = None
|
|
113
121
|
|
|
114
|
-
prog,
|
|
122
|
+
prog, prog_src = _prog_finder.normalize(prog, path, parent, no_sys_argv, command)
|
|
115
123
|
return cls(
|
|
116
124
|
parent=parent,
|
|
117
125
|
path=path,
|
|
@@ -119,7 +127,7 @@ class ProgramMetadata:
|
|
|
119
127
|
module=g.get('__module__'),
|
|
120
128
|
command=command.__qualname__,
|
|
121
129
|
prog=prog,
|
|
122
|
-
|
|
130
|
+
prog_src=prog_src,
|
|
123
131
|
url=url or g.get('__url__'),
|
|
124
132
|
docs_url=docs_url or _docs_url_from_repo_url(url) or _docs_url_from_repo_url(g.get('__url__')),
|
|
125
133
|
email=email or g.get('__author_email__'),
|
|
@@ -164,7 +172,7 @@ def _repr(obj, indent=0) -> str:
|
|
|
164
172
|
if not isinstance(obj, ProgramMetadata):
|
|
165
173
|
return repr(obj)
|
|
166
174
|
|
|
167
|
-
field_dict = {field: getattr(obj, field) for field in obj._fields}
|
|
175
|
+
field_dict = {field: getattr(obj, field) for field in sorted(obj._fields)}
|
|
168
176
|
prev_str = ' ' * indent
|
|
169
177
|
indent += 4
|
|
170
178
|
indent_str = ' ' * indent
|
|
@@ -172,32 +180,99 @@ def _repr(obj, indent=0) -> str:
|
|
|
172
180
|
return f'<{obj.__class__.__name__}(\n{fields_str}\n{prev_str})>'
|
|
173
181
|
|
|
174
182
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
class ProgFinder:
|
|
184
|
+
@cached_property
|
|
185
|
+
def mod_obj_prog_map(self) -> Dict[str, Dict[str, str]]:
|
|
186
|
+
mod_obj_prog_map = defaultdict(dict)
|
|
187
|
+
for entry_point in self._get_console_scripts():
|
|
188
|
+
module, obj = map(str.strip, entry_point.value.split(':', 1))
|
|
189
|
+
obj = obj.split('[', 1)[0].strip() # Strip extras, if any
|
|
190
|
+
mod_obj_prog_map[module][obj] = entry_point.name
|
|
191
|
+
|
|
192
|
+
mod_obj_prog_map.default_factory = None # Disable automatic defaults
|
|
193
|
+
return mod_obj_prog_map
|
|
194
|
+
|
|
195
|
+
@classmethod
|
|
196
|
+
def _get_console_scripts(cls) -> Tuple[EntryPoint, ...]:
|
|
181
197
|
try:
|
|
182
|
-
|
|
183
|
-
except
|
|
184
|
-
|
|
198
|
+
return entry_points(group='console_scripts')
|
|
199
|
+
except TypeError: # Python 3.8 or 3.9
|
|
200
|
+
return entry_points()['console_scripts']
|
|
201
|
+
|
|
202
|
+
def normalize(
|
|
203
|
+
self,
|
|
204
|
+
prog: OptStr,
|
|
205
|
+
cmd_path: Path,
|
|
206
|
+
parent: Optional[ProgramMetadata],
|
|
207
|
+
no_sys_argv: Bool,
|
|
208
|
+
command: CommandType,
|
|
209
|
+
) -> Tuple[OptStr, str]:
|
|
210
|
+
if prog:
|
|
211
|
+
return prog, 'class kwargs'
|
|
212
|
+
|
|
213
|
+
ep_name = self._from_entry_point(command)
|
|
214
|
+
if ep_name:
|
|
215
|
+
return ep_name, 'entry_points'
|
|
216
|
+
|
|
217
|
+
if no_sys_argv is None:
|
|
218
|
+
try:
|
|
219
|
+
no_sys_argv = not ctx.allow_argv_prog
|
|
220
|
+
except NoActiveContext:
|
|
221
|
+
no_sys_argv = False
|
|
222
|
+
|
|
223
|
+
# if parent and parent.prog != parent.path.name and (not no_sys_argv or not parent.prog_from_sys_argv):
|
|
224
|
+
# return parent.prog, parent.prog_from_sys_argv
|
|
225
|
+
if parent and parent.prog != parent.path.name and (not no_sys_argv or parent.prog_src != 'sys.argv'):
|
|
226
|
+
return parent.prog, parent.prog_src
|
|
227
|
+
elif not no_sys_argv:
|
|
228
|
+
argv_name = self._from_sys_argv()
|
|
229
|
+
if argv_name:
|
|
230
|
+
return argv_name, 'sys.argv'
|
|
231
|
+
|
|
232
|
+
return cmd_path.name, 'path'
|
|
233
|
+
|
|
234
|
+
def _from_entry_point(self, command: CommandType) -> OptStr:
|
|
235
|
+
main_mod = 'cli_command_parser.commands'
|
|
236
|
+
for prog, obj, obj_mod, obj_name in self._iter_entry_point_candidates(command):
|
|
237
|
+
if obj is command or (obj_mod == main_mod and obj_name == 'main'):
|
|
238
|
+
return prog
|
|
239
|
+
|
|
240
|
+
return None
|
|
185
241
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
242
|
+
def _iter_entry_point_candidates(self, command: CommandType):
|
|
243
|
+
try:
|
|
244
|
+
# TODO: This likely won't work for a base command in one module, sub commands defined in separate modules,
|
|
245
|
+
# and main imported from cli_command_parser in the package's __init__/__main__ module...
|
|
246
|
+
obj_prog_map = self.mod_obj_prog_map[command.__module__]
|
|
247
|
+
module = modules[command.__module__]
|
|
248
|
+
except KeyError as e:
|
|
249
|
+
pass
|
|
250
|
+
else:
|
|
251
|
+
for obj_name, prog in obj_prog_map.items():
|
|
252
|
+
base_name = obj_name.split('.', 1)[0]
|
|
253
|
+
try:
|
|
254
|
+
obj = getattr(module, base_name)
|
|
255
|
+
except AttributeError:
|
|
256
|
+
pass
|
|
257
|
+
else:
|
|
258
|
+
yield prog, obj, getattr(obj, '__module__', ''), getattr(obj, '__name__', '')
|
|
259
|
+
|
|
260
|
+
def _from_sys_argv(self) -> OptStr:
|
|
189
261
|
try:
|
|
190
262
|
ctx_prog = ctx.prog
|
|
191
263
|
except NoActiveContext:
|
|
192
|
-
|
|
264
|
+
return None
|
|
193
265
|
|
|
194
266
|
if ctx_prog:
|
|
195
267
|
path = Path(ctx_prog)
|
|
196
268
|
# Windows allows invocation without .exe - assume a file with an extension is a match
|
|
197
269
|
if path.exists() or next(path.parent.glob(f'{path.name}.???'), None) is not None:
|
|
198
|
-
return path.name
|
|
270
|
+
return path.name
|
|
271
|
+
|
|
272
|
+
return None
|
|
273
|
+
|
|
199
274
|
|
|
200
|
-
|
|
275
|
+
_prog_finder = ProgFinder()
|
|
201
276
|
|
|
202
277
|
|
|
203
278
|
def _path_and_globals(command: CommandType, path: Path = None) -> Tuple[Path, Dict[str, Any]]:
|
|
@@ -22,7 +22,7 @@ except ImportError:
|
|
|
22
22
|
|
|
23
23
|
from ..annotations import get_descriptor_value_type
|
|
24
24
|
from ..config import CommandConfig, OptionNameMode, AllowLeadingDash
|
|
25
|
-
from ..context import Context, ctx, get_current_context
|
|
25
|
+
from ..context import Context, ctx, get_current_context
|
|
26
26
|
from ..exceptions import ParameterDefinitionError, BadArgument, MissingArgument, InvalidChoice
|
|
27
27
|
from ..exceptions import ParamUsageError, NoActiveContext, UnsupportedAction
|
|
28
28
|
from ..inputs import InputType, normalize_input_type
|
|
@@ -277,7 +277,7 @@ class Parameter(ParamBase, Generic[T_co], ABC):
|
|
|
277
277
|
if show_default is not None:
|
|
278
278
|
self.show_default = show_default
|
|
279
279
|
|
|
280
|
-
def _init_value_factory(self
|
|
280
|
+
def _init_value_factory(self):
|
|
281
281
|
return _NotSet
|
|
282
282
|
|
|
283
283
|
def __set_name__(self, command: CommandCls, name: str):
|
|
@@ -489,10 +489,10 @@ class BasicActionMixin:
|
|
|
489
489
|
nargs: Nargs
|
|
490
490
|
type: Optional[Callable]
|
|
491
491
|
|
|
492
|
-
def _init_value_factory(self
|
|
492
|
+
def _init_value_factory(self):
|
|
493
493
|
if self.action == 'append':
|
|
494
494
|
return []
|
|
495
|
-
return super()._init_value_factory(
|
|
495
|
+
return super()._init_value_factory() # noqa
|
|
496
496
|
|
|
497
497
|
@parameter_action
|
|
498
498
|
def store(self: Parameter, value: T_co):
|
|
@@ -524,7 +524,7 @@ class BasicActionMixin:
|
|
|
524
524
|
if not values:
|
|
525
525
|
return values
|
|
526
526
|
|
|
527
|
-
ctx.set_parsed_value(self, self._init_value_factory(
|
|
527
|
+
ctx.set_parsed_value(self, self._init_value_factory())
|
|
528
528
|
ctx._provided[self] = 0
|
|
529
529
|
return values
|
|
530
530
|
|
|
@@ -8,10 +8,10 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from functools import partial
|
|
10
10
|
from string import whitespace, printable
|
|
11
|
-
from typing import Type, TypeVar, Generic, Optional, Callable, Union, Collection, Mapping, Dict
|
|
11
|
+
from typing import Type, TypeVar, Generic, Optional, Callable, Union, Collection, Mapping, NoReturn, Dict
|
|
12
12
|
from types import MethodType
|
|
13
13
|
|
|
14
|
-
from ..context import ctx
|
|
14
|
+
from ..context import ctx
|
|
15
15
|
from ..exceptions import ParameterDefinitionError, BadArgument, MissingArgument, InvalidChoice, CommandDefinitionError
|
|
16
16
|
from ..formatting.utils import format_help_entry
|
|
17
17
|
from ..nargs import Nargs
|
|
@@ -100,7 +100,7 @@ class ChoiceMap(BasePositional[str], Generic[T]):
|
|
|
100
100
|
self.description = description
|
|
101
101
|
self.choices = {}
|
|
102
102
|
|
|
103
|
-
def _init_value_factory(self
|
|
103
|
+
def _init_value_factory(self):
|
|
104
104
|
return []
|
|
105
105
|
|
|
106
106
|
# region Choice Registration
|
|
@@ -134,6 +134,9 @@ class ChoiceMap(BasePositional[str], Generic[T]):
|
|
|
134
134
|
prefix = 'Invalid default' if choice is None else f'Invalid choice={choice!r} for'
|
|
135
135
|
raise CommandDefinitionError(f'{prefix} target={target!r} - already assigned to {existing}')
|
|
136
136
|
|
|
137
|
+
def _no_choices_error(self) -> NoReturn:
|
|
138
|
+
raise CommandDefinitionError(f'No choices were registered for {self}')
|
|
139
|
+
|
|
137
140
|
# endregion
|
|
138
141
|
|
|
139
142
|
# region Argument Handling
|
|
@@ -151,33 +154,34 @@ class ChoiceMap(BasePositional[str], Generic[T]):
|
|
|
151
154
|
|
|
152
155
|
def validate(self, value: str):
|
|
153
156
|
choices = self.choices
|
|
154
|
-
if choices:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
raise BadArgument(self, f'invalid value={value!r}')
|
|
157
|
+
if not choices:
|
|
158
|
+
self._no_choices_error()
|
|
159
|
+
|
|
160
|
+
values = (*ctx.get_parsed_value(self), value)
|
|
161
|
+
choice = ' '.join(values)
|
|
162
|
+
if choice in choices:
|
|
163
|
+
return
|
|
164
|
+
elif len(values) > self.nargs.max:
|
|
165
|
+
raise BadArgument(self, 'too many values')
|
|
166
|
+
prefix = choice + ' '
|
|
167
|
+
if not any(c.startswith(prefix) for c in choices if c):
|
|
168
|
+
raise InvalidChoice(self, prefix[:-1], choices)
|
|
167
169
|
|
|
168
170
|
def result_value(self) -> OptStr:
|
|
169
171
|
choices = self.choices
|
|
170
172
|
if not choices:
|
|
171
|
-
|
|
173
|
+
self._no_choices_error()
|
|
172
174
|
|
|
173
175
|
values = ctx.get_parsed_value(self)
|
|
174
176
|
if not values:
|
|
175
177
|
if None in choices:
|
|
176
178
|
return None
|
|
177
179
|
raise MissingArgument(self)
|
|
180
|
+
|
|
178
181
|
val_count = len(values)
|
|
179
182
|
if val_count not in self.nargs:
|
|
180
183
|
raise BadArgument(self, f'expected nargs={self.nargs} values but found {val_count}')
|
|
184
|
+
|
|
181
185
|
choice = ' '.join(values)
|
|
182
186
|
if choice not in choices:
|
|
183
187
|
raise InvalidChoice(self, choice, choices)
|
|
@@ -293,6 +297,9 @@ class SubCommand(ChoiceMap[CommandCls], title='Subcommands', choice_validation_e
|
|
|
293
297
|
else:
|
|
294
298
|
return self.register_command(choice, command_or_choice, help=help) # noqa
|
|
295
299
|
|
|
300
|
+
def _no_choices_error(self) -> NoReturn:
|
|
301
|
+
raise CommandDefinitionError(f'{ctx.command}.{self.name} = {self} has no sub Commands')
|
|
302
|
+
|
|
296
303
|
|
|
297
304
|
class Action(ChoiceMap[MethodType], title='Actions'):
|
|
298
305
|
"""
|
|
@@ -10,7 +10,7 @@ from abc import ABC
|
|
|
10
10
|
from functools import partial, update_wrapper
|
|
11
11
|
from typing import Any, Optional, Callable, Sequence, Iterator, Union, TypeVar, Tuple
|
|
12
12
|
|
|
13
|
-
from ..context import ctx
|
|
13
|
+
from ..context import ctx
|
|
14
14
|
from ..exceptions import ParameterDefinitionError, BadArgument, CommandDefinitionError, ParamUsageError
|
|
15
15
|
from ..inputs import normalize_input_type
|
|
16
16
|
from ..nargs import Nargs, NargsValue
|
|
@@ -116,7 +116,7 @@ class _Flag(BaseOption[T_co], ABC):
|
|
|
116
116
|
raise TypeError(f"{self.__class__.__name__}.__init__() got an unexpected keyword argument: 'metavar'")
|
|
117
117
|
super().__init__(*option_strs, **kwargs)
|
|
118
118
|
|
|
119
|
-
def _init_value_factory(self
|
|
119
|
+
def _init_value_factory(self):
|
|
120
120
|
if self.action == 'store_const':
|
|
121
121
|
return self.default
|
|
122
122
|
else:
|
|
@@ -397,7 +397,7 @@ class Counter(BaseOption[int], accepts_values=True, accepts_none=True):
|
|
|
397
397
|
super().__init__(*option_strs, action=action, default=default, **kwargs)
|
|
398
398
|
self.const = const
|
|
399
399
|
|
|
400
|
-
def _init_value_factory(self
|
|
400
|
+
def _init_value_factory(self):
|
|
401
401
|
return self.default
|
|
402
402
|
|
|
403
403
|
def prepare_value(self, value: Optional[str], short_combo: bool = False, pre_action: bool = False) -> int:
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parser.py
RENAMED
|
@@ -11,15 +11,16 @@ from collections import deque
|
|
|
11
11
|
from os import environ
|
|
12
12
|
from typing import TYPE_CHECKING, Optional, Union, Any, Deque, List
|
|
13
13
|
|
|
14
|
-
from .context import ActionPhase, Context
|
|
14
|
+
from .context import ActionPhase, Context
|
|
15
15
|
from .exceptions import UsageError, ParamUsageError, NoSuchOption, MissingArgument, ParamsMissing
|
|
16
|
-
from .exceptions import
|
|
16
|
+
from .exceptions import Backtrack, UnsupportedAction
|
|
17
17
|
from .nargs import REMAINDER, nargs_max_sum, nargs_min_sum
|
|
18
18
|
from .parse_tree import PosNode
|
|
19
19
|
from .parameters.base import BasicActionMixin, Parameter, BasePositional, BaseOption
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from .command_parameters import CommandParameters
|
|
23
|
+
from .config import CommandConfig
|
|
23
24
|
from .typing import CommandType
|
|
24
25
|
|
|
25
26
|
__all__ = ['CommandParser']
|
|
@@ -29,72 +30,48 @@ __all__ = ['CommandParser']
|
|
|
29
30
|
class CommandParser:
|
|
30
31
|
"""Stateful parser used for a single pass of argument parsing"""
|
|
31
32
|
|
|
32
|
-
__slots__ = ('_last', 'arg_deque', '
|
|
33
|
+
__slots__ = ('_last', 'arg_deque', 'config', 'deferred', 'params', 'positionals')
|
|
33
34
|
|
|
34
35
|
arg_deque: Optional[Deque[str]]
|
|
36
|
+
config: CommandConfig
|
|
35
37
|
deferred: Optional[List[str]]
|
|
36
38
|
params: CommandParameters
|
|
37
39
|
positionals: List[BasePositional]
|
|
38
40
|
_last: Optional[Parameter]
|
|
39
41
|
|
|
40
|
-
def __init__(self, ctx: Context):
|
|
42
|
+
def __init__(self, ctx: Context, params: CommandParameters, config: CommandConfig):
|
|
41
43
|
self._last = None
|
|
42
|
-
self.
|
|
43
|
-
self.
|
|
44
|
-
self.
|
|
45
|
-
if
|
|
44
|
+
self.params = params
|
|
45
|
+
self.positionals = params.all_positionals.copy()
|
|
46
|
+
self.config = config
|
|
47
|
+
if config.reject_ambiguous_pos_combos:
|
|
46
48
|
PosNode.build_tree(ctx.command)
|
|
47
49
|
|
|
48
50
|
@classmethod
|
|
49
|
-
def
|
|
51
|
+
def parse_args_and_get_next_cmd(cls, ctx: Context) -> Optional[CommandType]:
|
|
50
52
|
try:
|
|
51
|
-
|
|
53
|
+
return cls(ctx, ctx.params, ctx.config).get_next_cmd(ctx)
|
|
52
54
|
except UsageError:
|
|
53
|
-
ctx.state = ParseState.FAILED
|
|
54
55
|
if not ctx.categorized_action_flags[ActionPhase.PRE_INIT]:
|
|
55
56
|
raise
|
|
56
57
|
return None
|
|
57
|
-
except Exception:
|
|
58
|
-
ctx.state = ParseState.FAILED
|
|
59
|
-
raise
|
|
60
|
-
else:
|
|
61
|
-
if ctx.state == ParseState.INITIAL:
|
|
62
|
-
ctx.state = ParseState.COMPLETE
|
|
63
|
-
return parsed
|
|
64
|
-
|
|
65
|
-
@classmethod
|
|
66
|
-
def __parse_args(cls, ctx: Context) -> Optional[CommandType]:
|
|
67
|
-
params = ctx.params
|
|
68
|
-
sub_cmd_param = params.sub_command
|
|
69
|
-
if sub_cmd_param and not sub_cmd_param.choices:
|
|
70
|
-
raise CommandDefinitionError(f'{ctx.command}.{sub_cmd_param.name} = {sub_cmd_param} has no sub Commands')
|
|
71
58
|
|
|
72
|
-
|
|
59
|
+
def get_next_cmd(self, ctx: Context) -> Optional[CommandType]:
|
|
60
|
+
self._parse_args(ctx)
|
|
61
|
+
params = self.params
|
|
73
62
|
params.validate_groups()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if missing and next_cmd.__class__.parent(next_cmd) is not ctx.command:
|
|
79
|
-
ctx.state = ParseState.FAILED
|
|
80
|
-
if ctx.categorized_action_flags[ActionPhase.PRE_INIT]:
|
|
81
|
-
return None
|
|
63
|
+
missing = ctx.get_missing()
|
|
64
|
+
no_pre_init_action = not ctx.categorized_action_flags[ActionPhase.PRE_INIT]
|
|
65
|
+
next_cmd = params.sub_command.target() if params.sub_command else None
|
|
66
|
+
if next_cmd is not None:
|
|
67
|
+
if missing and no_pre_init_action and next_cmd.__class__.parent(next_cmd) is not ctx.command:
|
|
82
68
|
raise ParamsMissing(missing)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
missing = cls._missing(params, ctx)
|
|
86
|
-
if missing and not ctx.config.allow_missing and (not params.action or params.action not in missing):
|
|
87
|
-
# Action is excluded because it provides a better error message
|
|
88
|
-
if not ctx.categorized_action_flags[ActionPhase.PRE_INIT]:
|
|
69
|
+
elif missing and not ctx.config.allow_missing and (not params.action or params.action not in missing):
|
|
70
|
+
if no_pre_init_action:
|
|
89
71
|
raise ParamsMissing(missing)
|
|
90
72
|
elif ctx.remaining and not ctx.config.ignore_unknown:
|
|
91
|
-
raise NoSuchOption('unrecognized arguments: {
|
|
92
|
-
|
|
93
|
-
return None
|
|
94
|
-
|
|
95
|
-
@classmethod
|
|
96
|
-
def _missing(cls, params: CommandParameters, ctx: Context) -> List[Parameter]:
|
|
97
|
-
return [p for p in params.required_check_params() if ctx.num_provided(p) == 0]
|
|
73
|
+
raise NoSuchOption(f'unrecognized arguments: {" ".join(ctx.remaining)}') from None
|
|
74
|
+
return next_cmd
|
|
98
75
|
|
|
99
76
|
def _parse_args(self, ctx: Context):
|
|
100
77
|
self.arg_deque = arg_deque = self.handle_pass_thru(ctx)
|
|
@@ -254,7 +231,7 @@ class CommandParser:
|
|
|
254
231
|
:param found: The number of values that were consumed by the given Parameter
|
|
255
232
|
:return: The updated found count, if backtracking was possible, otherwise the unmodified found count
|
|
256
233
|
"""
|
|
257
|
-
if not self.
|
|
234
|
+
if not self.config.allow_backtrack or not self.positionals or found < 2:
|
|
258
235
|
return found
|
|
259
236
|
|
|
260
237
|
can_pop = param.can_pop_counts()
|
|
@@ -269,7 +246,7 @@ class CommandParser:
|
|
|
269
246
|
"""
|
|
270
247
|
Similar to :meth:`._maybe_backtrack`, but allows backtracking even after starting to process a Positional.
|
|
271
248
|
"""
|
|
272
|
-
if not self.
|
|
249
|
+
if not self.config.allow_backtrack:
|
|
273
250
|
return
|
|
274
251
|
|
|
275
252
|
can_pop = self._last.can_pop_counts()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cli-command-parser
|
|
3
|
-
Version: 2023.4.
|
|
3
|
+
Version: 2023.4.16
|
|
4
4
|
Summary: CLI Command Parser
|
|
5
5
|
Home-page: https://github.com/dskrypa/cli_command_parser
|
|
6
6
|
Author: Doug Skrypa
|
|
@@ -110,6 +110,16 @@ with optional dependencies::
|
|
|
110
110
|
|
|
111
111
|
$ pip install -U cli-command-parser[wcwidth]
|
|
112
112
|
|
|
113
|
+
|
|
114
|
+
Python Version Compatibility
|
|
115
|
+
============================
|
|
116
|
+
|
|
117
|
+
Python versions 3.7 and above are currently supported. CLI Command Parser will no longer support 3.7 after 2023-04-30,
|
|
118
|
+
ahead of the `official end of support for 3.7 on 2023-06-27 <https://devguide.python.org/versions/>`__.
|
|
119
|
+
|
|
120
|
+
When using 3.7 or 3.8, some additional packages that backport functionality that was added in later Python versions
|
|
121
|
+
are required for compatibility.
|
|
122
|
+
|
|
113
123
|
To use the argparse to cli-command-parser conversion script with Python 3.7 or 3.8, there is a dependency on
|
|
114
124
|
`astunparse <https://astunparse.readthedocs.io>`__. If you are using Python 3.9 or above, then ``astunparse`` is not
|
|
115
125
|
necessary because the relevant code was added to the stdlib ``ast`` module. If you're unsure, you can install
|
|
@@ -81,6 +81,16 @@ with optional dependencies::
|
|
|
81
81
|
|
|
82
82
|
$ pip install -U cli-command-parser[wcwidth]
|
|
83
83
|
|
|
84
|
+
|
|
85
|
+
Python Version Compatibility
|
|
86
|
+
============================
|
|
87
|
+
|
|
88
|
+
Python versions 3.7 and above are currently supported. CLI Command Parser will no longer support 3.7 after 2023-04-30,
|
|
89
|
+
ahead of the `official end of support for 3.7 on 2023-06-27 <https://devguide.python.org/versions/>`__.
|
|
90
|
+
|
|
91
|
+
When using 3.7 or 3.8, some additional packages that backport functionality that was added in later Python versions
|
|
92
|
+
are required for compatibility.
|
|
93
|
+
|
|
84
94
|
To use the argparse to cli-command-parser conversion script with Python 3.7 or 3.8, there is a dependency on
|
|
85
95
|
`astunparse <https://astunparse.readthedocs.io>`__. If you are using Python 3.9 or above, then ``astunparse`` is not
|
|
86
96
|
necessary because the relevant code was added to the stdlib ``ast`` module. If you're unsure, you can install
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/__init__.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/__main__.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/actions.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/annotations.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/compat.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/core.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/files.py
RENAMED
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/time.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/inputs/utils.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/nargs.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/parse_tree.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/testing.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/typing.py
RENAMED
|
File without changes
|
{cli_command_parser-2023.4.15 → cli_command_parser-2023.4.16}/lib/cli_command_parser/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|