cli-command-parser 2024.8.17__tar.gz → 2024.11.2__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.8.17/lib/cli_command_parser.egg-info → cli_command_parser-2024.11.2}/PKG-INFO +6 -18
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/__version__.py +1 -1
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/annotations.py +1 -10
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/compat.py +0 -10
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/utils.py +2 -19
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/core.py +1 -1
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/documentation.py +1 -1
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/formatting/commands.py +11 -6
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/formatting/params.py +17 -10
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/formatting/restructured_text.py +94 -55
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/metadata.py +69 -4
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/choice_map.py +2 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/groups.py +7 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2/lib/cli_command_parser.egg-info}/PKG-INFO +6 -18
- cli_command_parser-2024.11.2/lib/cli_command_parser.egg-info/requires.txt +3 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/readme.rst +3 -13
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/requirements-dev.txt +1 -1
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/setup.cfg +2 -4
- cli_command_parser-2024.8.17/lib/cli_command_parser.egg-info/requires.txt +0 -8
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/LICENSE +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/MANIFEST.in +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/entry_points.txt +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/__init__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/__main__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/command_parameters.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/commands.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/config.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/context.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/__init__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/__main__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/argparse_ast.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/argparse_utils.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/cli.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/command_builder.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/conversion/visitor.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/error_handling.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/exceptions.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/formatting/__init__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/formatting/utils.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/__init__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/base.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/choices.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/exceptions.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/files.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/numeric.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/patterns.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/time.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/utils.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/nargs.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/__init__.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/actions.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/base.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/option_strings.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/options.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/pass_thru.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parameters/positionals.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parse_tree.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parser.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/testing.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/typing.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/utils.py +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser.egg-info/SOURCES.txt +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser.egg-info/dependency_links.txt +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser.egg-info/entry_points.txt +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser.egg-info/top_level.txt +0 -0
- {cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cli_command_parser
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.11.2
|
|
4
4
|
Summary: CLI Command Parser
|
|
5
5
|
Home-page: https://github.com/dskrypa/cli_command_parser
|
|
6
6
|
Author: Doug Skrypa
|
|
@@ -16,27 +16,25 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
24
|
Classifier: Topic :: Software Development :: User Interfaces
|
|
25
25
|
Classifier: Topic :: Text Processing
|
|
26
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
27
|
Description-Content-Type: text/x-rst
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
Provides-Extra: wcwidth
|
|
30
30
|
Requires-Dist: wcwidth; extra == "wcwidth"
|
|
31
|
-
Provides-Extra: conversion
|
|
32
|
-
Requires-Dist: astunparse; python_version < "3.9" and extra == "conversion"
|
|
33
31
|
|
|
34
32
|
CLI Command Parser
|
|
35
33
|
##################
|
|
36
34
|
|
|
37
35
|
|downloads| |py_version| |coverage_badge| |build_status| |Ruff|
|
|
38
36
|
|
|
39
|
-
.. |py_version| image:: https://img.shields.io/badge/python-3.
|
|
37
|
+
.. |py_version| image:: https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20-blue
|
|
40
38
|
:target: https://pypi.org/project/cli-command-parser/
|
|
41
39
|
|
|
42
40
|
.. |coverage_badge| image:: https://codecov.io/gh/dskrypa/cli_command_parser/branch/main/graph/badge.svg
|
|
@@ -118,18 +116,8 @@ with optional dependencies::
|
|
|
118
116
|
Python Version Compatibility
|
|
119
117
|
============================
|
|
120
118
|
|
|
121
|
-
Python versions 3.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
When using Python 3.8, some additional packages that backport functionality that was added in later Python versions
|
|
125
|
-
are required for compatibility.
|
|
126
|
-
|
|
127
|
-
To use the argparse to cli-command-parser conversion script with Python 3.8, there is a dependency on
|
|
128
|
-
`astunparse <https://astunparse.readthedocs.io>`__. If you are using Python 3.9 or above, then ``astunparse`` is not
|
|
129
|
-
necessary because the relevant code was added to the stdlib ``ast`` module. If you're unsure, you can install
|
|
130
|
-
cli-command-parser with the following command to automatically handle whether that extra dependency is needed or not::
|
|
131
|
-
|
|
132
|
-
$ pip install -U cli-command-parser[conversion]
|
|
119
|
+
Python versions 3.9 and above are currently supported. The last release of CLI Command Parser that supported 3.8 was
|
|
120
|
+
2024-09-07. Support for Python 3.8 `officially ended on 2024-10-07 <https://devguide.python.org/versions/>`__.
|
|
133
121
|
|
|
134
122
|
|
|
135
123
|
Links
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/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.11.02'
|
|
5
5
|
__author__ = 'Doug Skrypa'
|
|
6
6
|
__author_email__ = 'dskrypa@gmail.com'
|
|
7
7
|
__license__ = 'Apache 2.0'
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/annotations.py
RENAMED
|
@@ -7,7 +7,7 @@ Utilities for extracting types from annotations.
|
|
|
7
7
|
from collections.abc import Collection, Iterable
|
|
8
8
|
from functools import lru_cache
|
|
9
9
|
from inspect import isclass
|
|
10
|
-
from typing import
|
|
10
|
+
from typing import Optional, Union, get_args, get_origin, get_type_hints as _get_type_hints
|
|
11
11
|
|
|
12
12
|
try:
|
|
13
13
|
from types import NoneType
|
|
@@ -42,15 +42,6 @@ def get_annotation_value_type(annotation, from_union: bool = True, from_collecti
|
|
|
42
42
|
return None
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def get_args(annotation) -> tuple:
|
|
46
|
-
"""
|
|
47
|
-
Wrapper around :func:`python:typing.get_args` for 3.7~8 compatibility, to make it behave more like it does in 3.9+
|
|
48
|
-
"""
|
|
49
|
-
if getattr(annotation, '_special', False): # 3.7-3.8 generic collection alias with no content types
|
|
50
|
-
return ()
|
|
51
|
-
return _get_args(annotation)
|
|
52
|
-
|
|
53
|
-
|
|
54
45
|
def _type_from_union(annotation) -> Optional[type]:
|
|
55
46
|
args = get_args(annotation)
|
|
56
47
|
# Note: Unions of a single argument return the argument; i.e., Union[T] returns T, so the len can never be 1
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/compat.py
RENAMED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Compatibility / Patch module - used to back-port features to Python 3.7 and to avoid breaking changes in Enum/Flag in
|
|
3
|
-
3.11.
|
|
4
|
-
|
|
5
|
-
Contains stdlib CPython functions / classes from Python 3.8 and 3.10.
|
|
6
|
-
|
|
7
2
|
The :class:`WCTextWrapper` in this module extends the stdlib :class:`python:textwrap.TextWrapper` to support wide
|
|
8
3
|
characters.
|
|
9
4
|
"""
|
|
@@ -16,8 +11,6 @@ from .utils import wcswidth
|
|
|
16
11
|
|
|
17
12
|
__all__ = ['WCTextWrapper']
|
|
18
13
|
|
|
19
|
-
# region textwrap
|
|
20
|
-
|
|
21
14
|
|
|
22
15
|
class WCTextWrapper(TextWrapper):
|
|
23
16
|
"""
|
|
@@ -119,6 +112,3 @@ class WCTextWrapper(TextWrapper):
|
|
|
119
112
|
break
|
|
120
113
|
|
|
121
114
|
return lines
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
# endregion
|
|
@@ -1,24 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from ast import AST,
|
|
4
|
-
|
|
5
|
-
try:
|
|
6
|
-
from ast import unparse
|
|
7
|
-
except ImportError: # added in 3.9
|
|
8
|
-
try:
|
|
9
|
-
from astunparse import unparse as _unparse
|
|
10
|
-
except ImportError as e:
|
|
11
|
-
raise RuntimeError(
|
|
12
|
-
'Missing required dependency: astunparse (only required in Python 3.8 and below'
|
|
13
|
-
' - upgrade to 3.9 or above to avoid this dependency)'
|
|
14
|
-
)
|
|
15
|
-
else:
|
|
16
|
-
|
|
17
|
-
def unparse(node):
|
|
18
|
-
return ''.join(_unparse(node).splitlines())
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
from typing import Union, Iterator, List as _List
|
|
3
|
+
from ast import AST, Attribute, Call, Dict, List, Name, Set, Tuple, expr, unparse
|
|
4
|
+
from typing import Iterator, List as _List, Union
|
|
22
5
|
|
|
23
6
|
__all__ = ['get_name_repr', 'iter_module_parents', 'collection_contents']
|
|
24
7
|
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/core.py
RENAMED
|
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
|
|
|
28
28
|
__all__ = ['CommandMeta', 'get_parent', 'get_config', 'get_params', 'get_metadata', 'get_top_level_commands']
|
|
29
29
|
|
|
30
30
|
_NotSet = object()
|
|
31
|
-
META_KEYS = {'prog', 'usage', 'description', 'epilog', 'doc_name'}
|
|
31
|
+
META_KEYS = {'prog', 'usage', 'description', 'epilog', 'doc_name', 'path', 'url', 'docs_url', 'email', 'version'}
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
class CommandMeta(ABCMeta, type):
|
|
@@ -372,6 +372,6 @@ class RstWriter:
|
|
|
372
372
|
path = target_dir.joinpath(name + self.ext)
|
|
373
373
|
log.debug(f'{prefix} {path.as_posix()}')
|
|
374
374
|
if not self.dry_run:
|
|
375
|
-
# Path.write_text on 3.
|
|
375
|
+
# Path.write_text on 3.9 does not support `newline`
|
|
376
376
|
with path.open('w', encoding=self.encoding, newline=self.newline) as f:
|
|
377
377
|
f.write(content)
|
|
@@ -12,9 +12,10 @@ from typing import TYPE_CHECKING, Callable, Iterable, Iterator, Optional, Type,
|
|
|
12
12
|
|
|
13
13
|
from ..context import NoActiveContext, ctx
|
|
14
14
|
from ..core import get_metadata, get_params
|
|
15
|
+
from ..parameters.choice_map import ChoiceMap
|
|
15
16
|
from ..parameters.groups import ParamGroup
|
|
16
17
|
from ..utils import _NotSet, camel_to_snake_case
|
|
17
|
-
from .restructured_text import
|
|
18
|
+
from .restructured_text import spaced_rst_header
|
|
18
19
|
from .utils import PartWrapper
|
|
19
20
|
|
|
20
21
|
if TYPE_CHECKING:
|
|
@@ -47,6 +48,7 @@ class CommandHelpFormatter:
|
|
|
47
48
|
for group in groups:
|
|
48
49
|
if group.group: # prevent duplicates
|
|
49
50
|
continue
|
|
51
|
+
|
|
50
52
|
if group.contains_positional:
|
|
51
53
|
self.pos_group.add(group)
|
|
52
54
|
else:
|
|
@@ -162,14 +164,17 @@ class CommandHelpFormatter:
|
|
|
162
164
|
yield description
|
|
163
165
|
yield ''
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
# subcommand sections
|
|
167
|
-
for group in self.groups:
|
|
167
|
+
if self.pos_group.show_in_help:
|
|
168
168
|
# TODO: Nested subcommands' local choices should not repeat the `subcommands` positional arguments section
|
|
169
169
|
# that includes the nested subcommand choice being documented
|
|
170
|
+
if len(members := self.pos_group.members) == 1 and isinstance(members[0], ChoiceMap):
|
|
171
|
+
yield from members[0].formatter.rst_table().iter_build() # noqa
|
|
172
|
+
else:
|
|
173
|
+
yield from self.pos_group.formatter.rst_table().iter_build()
|
|
174
|
+
|
|
175
|
+
for group in self.groups[1:]:
|
|
170
176
|
if group.show_in_help:
|
|
171
|
-
|
|
172
|
-
yield from table.iter_build()
|
|
177
|
+
yield from group.formatter.rst_table().iter_build()
|
|
173
178
|
|
|
174
179
|
if include_epilog and (epilog := self._meta.format_epilog(config.extended_epilog, allow_sys_argv)):
|
|
175
180
|
yield epilog
|
|
@@ -16,7 +16,7 @@ from ..core import get_config
|
|
|
16
16
|
from ..parameters import ParamGroup, PassThru, TriFlag
|
|
17
17
|
from ..parameters.base import BaseOption, BasePositional
|
|
18
18
|
from ..parameters.choice_map import Choice, ChoiceMap
|
|
19
|
-
from .restructured_text import RstTable
|
|
19
|
+
from .restructured_text import Cell, Row, RstTable
|
|
20
20
|
from .utils import _should_add_default, format_help_entry
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
@@ -223,6 +223,8 @@ class TriFlagHelpFormatter(OptionHelpFormatter, param_cls=TriFlag):
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
class ChoiceMapHelpFormatter(ParamHelpFormatter, param_cls=ChoiceMap):
|
|
226
|
+
"""Formatter for :class:`SubCommand` and :class:`Action` parameters (and any other params that extend ChoiceMap)"""
|
|
227
|
+
|
|
226
228
|
param: ChoiceMap
|
|
227
229
|
|
|
228
230
|
@cached_property
|
|
@@ -269,6 +271,7 @@ class ChoiceMapHelpFormatter(ParamHelpFormatter, param_cls=ChoiceMap):
|
|
|
269
271
|
|
|
270
272
|
def _format_rst_rows(self) -> Iterator[tuple[str, OptStr]]:
|
|
271
273
|
mode = ctx.config.cmd_alias_mode or SubcommandAliasHelpMode.ALIAS
|
|
274
|
+
# TODO: The subcommand names should link to their respective subcommand sections
|
|
272
275
|
for choice_group in self.choice_groups:
|
|
273
276
|
for choice, usage, description in choice_group.prepare(mode):
|
|
274
277
|
yield f'``{usage}``', description
|
|
@@ -492,15 +495,19 @@ class GroupHelpFormatter(ParamHelpFormatter, param_cls=ParamGroup): # noqa # p
|
|
|
492
495
|
table = RstTable(self.format_description())
|
|
493
496
|
# TODO: non-nested when config.show_group_tree is False; maybe separate options for rst vs help
|
|
494
497
|
for member in self.param.members:
|
|
495
|
-
if member.show_in_help:
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
498
|
+
if not member.show_in_help:
|
|
499
|
+
continue
|
|
500
|
+
|
|
501
|
+
formatter = member.formatter
|
|
502
|
+
try:
|
|
503
|
+
sub_table: RstTable = formatter.rst_table() # noqa
|
|
504
|
+
except AttributeError:
|
|
505
|
+
table.add_rows(formatter.rst_rows())
|
|
506
|
+
else:
|
|
507
|
+
table._add_row(Row([Cell(str(sub_table), ext_right=True), Cell()]))
|
|
508
|
+
# If a config option to switch to the old way is added later, the old approach:
|
|
509
|
+
# sub_table.show_title = False
|
|
510
|
+
# table.add_row(sub_table.title, str(sub_table))
|
|
504
511
|
|
|
505
512
|
return table
|
|
506
513
|
|
|
@@ -6,11 +6,8 @@ Utilities for formatting data using RST markup
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from itertools import starmap
|
|
10
9
|
from typing import TYPE_CHECKING, Any, Iterable, Iterator, Mapping, Sequence, TypeVar, Union
|
|
11
10
|
|
|
12
|
-
from .utils import line_iter
|
|
13
|
-
|
|
14
11
|
if TYPE_CHECKING:
|
|
15
12
|
from ..typing import Bool, OptStr, Strings
|
|
16
13
|
|
|
@@ -119,7 +116,7 @@ class RstTable:
|
|
|
119
116
|
body of this table.
|
|
120
117
|
"""
|
|
121
118
|
|
|
122
|
-
__slots__ = ('title', 'subtitle', 'show_title', 'use_table_directive', '
|
|
119
|
+
__slots__ = ('title', 'subtitle', 'show_title', 'use_table_directive', '_rows', '_widths', '_updated')
|
|
123
120
|
|
|
124
121
|
def __init__(
|
|
125
122
|
self,
|
|
@@ -134,8 +131,9 @@ class RstTable:
|
|
|
134
131
|
self.subtitle = subtitle
|
|
135
132
|
self.show_title = show_title
|
|
136
133
|
self.use_table_directive = use_table_directive
|
|
137
|
-
self.
|
|
138
|
-
self.
|
|
134
|
+
self._rows = []
|
|
135
|
+
self._widths = ()
|
|
136
|
+
self._updated = False
|
|
139
137
|
if headers:
|
|
140
138
|
self.add_row(*headers, header=True)
|
|
141
139
|
|
|
@@ -163,6 +161,13 @@ class RstTable:
|
|
|
163
161
|
table.add_kv_rows(data)
|
|
164
162
|
return table
|
|
165
163
|
|
|
164
|
+
@property
|
|
165
|
+
def widths(self) -> tuple[int, ...]:
|
|
166
|
+
if self._updated:
|
|
167
|
+
self._widths = tuple(max(col) for col in zip(*(row.widths() for row in self._rows)))
|
|
168
|
+
self._updated = False
|
|
169
|
+
return self._widths
|
|
170
|
+
|
|
166
171
|
def add_dict_rows(self, rows: RowMaps, columns: Sequence[T] = None, add_header: Bool = False):
|
|
167
172
|
"""Add a row for each dict in the given sequence of rows, where the keys represent the columns."""
|
|
168
173
|
if not columns:
|
|
@@ -180,8 +185,11 @@ class RstTable:
|
|
|
180
185
|
self.add_rows(data.items())
|
|
181
186
|
|
|
182
187
|
def add_rows(self, rows: Iterable[Iterable[OptStr]]):
|
|
183
|
-
for
|
|
184
|
-
|
|
188
|
+
self._add_rows(Row([Cell(c or '') for c in columns]) for columns in rows)
|
|
189
|
+
|
|
190
|
+
def _add_rows(self, rows: Iterable[Row]):
|
|
191
|
+
self._rows.extend(rows)
|
|
192
|
+
self._updated = True
|
|
185
193
|
|
|
186
194
|
def add_row(self, *columns: OptStr, index: int = None, header: bool = False):
|
|
187
195
|
"""
|
|
@@ -192,34 +200,18 @@ class RstTable:
|
|
|
192
200
|
the list of rows.
|
|
193
201
|
:param header: If True, this row will be treated as a header row. Does not affect insertion order.
|
|
194
202
|
"""
|
|
195
|
-
|
|
196
|
-
if self.widths:
|
|
197
|
-
self.widths = tuple(starmap(max, zip(self.widths, widths)))
|
|
198
|
-
else:
|
|
199
|
-
self.widths = tuple(widths)
|
|
203
|
+
self._add_row(Row([Cell(c or '') for c in columns], header), index)
|
|
200
204
|
|
|
201
|
-
|
|
205
|
+
def _add_row(self, row: Row, index: int = None):
|
|
202
206
|
if index is None:
|
|
203
|
-
self.
|
|
207
|
+
self._rows.append(row)
|
|
204
208
|
else:
|
|
205
|
-
self.
|
|
206
|
-
|
|
207
|
-
def bar(self, char: str = '-') -> str:
|
|
208
|
-
"""
|
|
209
|
-
:param char: The character to use for the bar. Defaults to ``-`` (for normal rows). Use ``=`` below a header
|
|
210
|
-
row. See :du_rst:`Grid Tables<grid-tables>` for more info.
|
|
211
|
-
:return: The formatted bar string
|
|
212
|
-
"""
|
|
213
|
-
pre = ' ' if self.use_table_directive else ''
|
|
214
|
-
return '+'.join([pre, *(char * (w + 2) for w in self.widths), ''])
|
|
215
|
-
|
|
216
|
-
def _get_row_format(self) -> str:
|
|
217
|
-
pre = ' ' if self.use_table_directive else ''
|
|
218
|
-
return '|'.join([pre, *(f' {{:<{w}s}} ' for w in self.widths), ''])
|
|
209
|
+
self._rows.insert(index, row)
|
|
210
|
+
self._updated = True
|
|
219
211
|
|
|
220
212
|
def __repr__(self) -> str:
|
|
221
213
|
return (
|
|
222
|
-
f'<RstTable[use_table_directive={self.use_table_directive}, rows={len(self.
|
|
214
|
+
f'<RstTable[use_table_directive={self.use_table_directive}, rows={len(self._rows)},'
|
|
223
215
|
f' title={self.title!r}, widths={self.widths}]>'
|
|
224
216
|
)
|
|
225
217
|
|
|
@@ -230,38 +222,85 @@ class RstTable:
|
|
|
230
222
|
yield ''
|
|
231
223
|
|
|
232
224
|
if self.use_table_directive:
|
|
233
|
-
options
|
|
234
|
-
yield from _rst_directive('table', options=options, check=True)
|
|
225
|
+
yield from _rst_directive('table', options={'subtitle': self.subtitle, 'widths': 'auto'}, check=True)
|
|
235
226
|
yield ''
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
for header, any_new_line, row in self.rows:
|
|
241
|
-
if any_new_line:
|
|
242
|
-
for line in line_iter(*row):
|
|
243
|
-
yield format_row(*line)
|
|
244
|
-
else:
|
|
245
|
-
yield format_row(*row)
|
|
246
|
-
|
|
247
|
-
yield header_bar if header else bar
|
|
227
|
+
for line in self._iter_render():
|
|
228
|
+
yield ' ' + line
|
|
229
|
+
else:
|
|
230
|
+
yield from self._iter_render()
|
|
248
231
|
|
|
249
232
|
yield ''
|
|
250
233
|
|
|
234
|
+
def _iter_render(self) -> Iterator[str]:
|
|
235
|
+
col_widths = self.widths
|
|
236
|
+
yield self._rows[0].render_upper_bar(col_widths)
|
|
237
|
+
for row in self._rows:
|
|
238
|
+
yield from row.render_lines(col_widths)
|
|
239
|
+
|
|
251
240
|
def __str__(self) -> str:
|
|
252
241
|
return '\n'.join(self.iter_build())
|
|
253
242
|
|
|
254
243
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
244
|
+
class Cell:
|
|
245
|
+
__slots__ = ('text', 'lines', 'width', 'height', 'brd_bottom', 'brd_right')
|
|
246
|
+
|
|
247
|
+
def __init__(self, text: str = '', *, ext_right: bool = False, ext_below: bool = False):
|
|
248
|
+
self.text = text
|
|
249
|
+
if text:
|
|
250
|
+
self.lines = text.splitlines()
|
|
251
|
+
self.width = max(map(len, self.lines))
|
|
252
|
+
self.height = len(self.lines)
|
|
264
253
|
else:
|
|
265
|
-
|
|
254
|
+
self.lines = ['']
|
|
255
|
+
self.width = 0
|
|
256
|
+
self.height = 1
|
|
257
|
+
|
|
258
|
+
self.brd_bottom = not ext_below
|
|
259
|
+
self.brd_right = not ext_right
|
|
260
|
+
|
|
261
|
+
def render_upper_bar(self, width: int) -> str:
|
|
262
|
+
return ('-' * (width + 2)) + ('+' if self.brd_bottom else '|')
|
|
263
|
+
|
|
264
|
+
def render_lower_bar(self, width: int, char: str = '-') -> str:
|
|
265
|
+
if not self.brd_bottom:
|
|
266
|
+
char = ' '
|
|
267
|
+
return (char * (width + 2)) + ('+' if self.brd_bottom or self.brd_right else ' ')
|
|
268
|
+
|
|
269
|
+
def render_lines(self, width: int, max_lines: int) -> Iterator[str]:
|
|
270
|
+
format_line = f' {{:<{width}s}} {"|" if self.brd_right else " "}'.format
|
|
271
|
+
for line in self.lines:
|
|
272
|
+
yield format_line(line)
|
|
273
|
+
|
|
274
|
+
for _ in range(self.height, max_lines):
|
|
275
|
+
yield format_line('')
|
|
276
|
+
|
|
277
|
+
def __repr__(self) -> str:
|
|
278
|
+
return f'<{self.__class__.__name__}[{self.text!r}, width={self.width}, height={self.height}]>'
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class Row:
|
|
282
|
+
__slots__ = ('cells', 'header')
|
|
283
|
+
|
|
284
|
+
def __init__(self, cells: list[Cell], header: bool = False):
|
|
285
|
+
self.cells = cells
|
|
286
|
+
self.header = header
|
|
287
|
+
|
|
288
|
+
def widths(self) -> Iterator[int]:
|
|
289
|
+
for cell in self.cells:
|
|
290
|
+
yield cell.width
|
|
291
|
+
|
|
292
|
+
def render_upper_bar(self, widths: Sequence[int]) -> str:
|
|
293
|
+
return '+' + ''.join(cell.render_upper_bar(w) for cell, w in zip(self.cells, widths))
|
|
294
|
+
|
|
295
|
+
def render_lower_bar(self, widths: Sequence[int]) -> str:
|
|
296
|
+
char = '=' if self.header else '-'
|
|
297
|
+
first = '+' if self.cells[0].brd_bottom else '|'
|
|
298
|
+
return first + ''.join(cell.render_lower_bar(w, char) for cell, w in zip(self.cells, widths))
|
|
299
|
+
|
|
300
|
+
def render_lines(self, widths: Sequence[int]) -> Iterator[str]:
|
|
301
|
+
max_lines = max(cell.height for cell in self.cells)
|
|
302
|
+
renderers = [cell.render_lines(w, max_lines) for cell, w in zip(self.cells, widths)]
|
|
303
|
+
for cell_strs in zip(*renderers):
|
|
304
|
+
yield '|' + ''.join(cell_strs)
|
|
266
305
|
|
|
267
|
-
|
|
306
|
+
yield self.render_lower_bar(widths)
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/metadata.py
RENAMED
|
@@ -7,6 +7,8 @@ Program metadata introspection for use in usage, help text, and documentation.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import json
|
|
11
|
+
import platform
|
|
10
12
|
from collections import defaultdict
|
|
11
13
|
from functools import cached_property
|
|
12
14
|
from importlib.metadata import Distribution, EntryPoint, entry_points
|
|
@@ -24,6 +26,7 @@ if TYPE_CHECKING:
|
|
|
24
26
|
|
|
25
27
|
__all__ = ['ProgramMetadata']
|
|
26
28
|
|
|
29
|
+
WINDOWS = platform.system().lower() == 'windows'
|
|
27
30
|
DEFAULT_FILE_NAME: str = 'UNKNOWN'
|
|
28
31
|
|
|
29
32
|
|
|
@@ -247,12 +250,14 @@ class ProgramMetadata:
|
|
|
247
250
|
|
|
248
251
|
def format_epilog(self, extended: Bool = True, allow_sys_argv: Bool = None) -> str:
|
|
249
252
|
parts = [self.epilog] if self.epilog else []
|
|
253
|
+
# TODO: Add support for epilog_format format string?
|
|
250
254
|
if parts and not extended:
|
|
251
255
|
return parts[0]
|
|
252
256
|
|
|
253
257
|
if version := self.version:
|
|
254
258
|
version = f' [ver. {version}]'
|
|
255
259
|
if self.email:
|
|
260
|
+
# TODO: add support_url metadata entry and use that instead of email, if present?
|
|
256
261
|
parts.append(f'Report {self.get_prog(allow_sys_argv)}{version} bugs to {self.email}')
|
|
257
262
|
if url := self.docs_url or self.url:
|
|
258
263
|
parts.append(f'Online documentation: {url}')
|
|
@@ -267,6 +272,7 @@ class ProgramMetadata:
|
|
|
267
272
|
return doc_str
|
|
268
273
|
|
|
269
274
|
def get_description(self, allow_inherited: Bool = True) -> OptStr:
|
|
275
|
+
# TODO: Description template for subcommands?
|
|
270
276
|
if description := self.description:
|
|
271
277
|
if not allow_inherited and (parent := self.parent) and (parent_description := parent.description): # noqa
|
|
272
278
|
return description if parent_description != description else None
|
|
@@ -301,7 +307,7 @@ class ProgFinder:
|
|
|
301
307
|
def _get_console_scripts(cls) -> tuple[EntryPoint, ...]:
|
|
302
308
|
try:
|
|
303
309
|
return entry_points(group='console_scripts') # noqa
|
|
304
|
-
except TypeError: # Python 3.
|
|
310
|
+
except TypeError: # Python 3.9
|
|
305
311
|
return entry_points()['console_scripts'] # noqa
|
|
306
312
|
|
|
307
313
|
def normalize(
|
|
@@ -382,10 +388,33 @@ class DistributionFinder:
|
|
|
382
388
|
self._dist_top_levels = {}
|
|
383
389
|
self._dist_urls = {}
|
|
384
390
|
|
|
391
|
+
@cached_property
|
|
392
|
+
def _all_distributions(self) -> tuple[dict[str, Distribution], dict[str, tuple[Distribution, Path]]]:
|
|
393
|
+
normal, editable = {}, {}
|
|
394
|
+
for dist in Distribution.discover():
|
|
395
|
+
# Note: Distribution.name was not added until 3.10, and it returns `self.metadata['Name']`
|
|
396
|
+
if not (name := dist.metadata.get('Name')):
|
|
397
|
+
continue
|
|
398
|
+
elif existing := normal.get(name):
|
|
399
|
+
if path := _get_editable_path(existing):
|
|
400
|
+
editable[name] = (dist, path)
|
|
401
|
+
elif path := _get_editable_path(dist):
|
|
402
|
+
editable[name] = (existing, path)
|
|
403
|
+
normal[name] = dist
|
|
404
|
+
else: # A third case is not really expected here...
|
|
405
|
+
normal[name] = dist
|
|
406
|
+
else:
|
|
407
|
+
normal[name] = dist
|
|
408
|
+
|
|
409
|
+
return normal, editable
|
|
410
|
+
|
|
385
411
|
@cached_property
|
|
386
412
|
def _distributions(self) -> dict[str, Distribution]:
|
|
387
|
-
|
|
388
|
-
|
|
413
|
+
return self._all_distributions[0]
|
|
414
|
+
|
|
415
|
+
@cached_property
|
|
416
|
+
def _editable_distributions(self) -> dict[str, tuple[Distribution, Path]]:
|
|
417
|
+
return self._all_distributions[1]
|
|
389
418
|
|
|
390
419
|
def _get_top_levels(self, dist_name: str, dist: Distribution) -> set[str]:
|
|
391
420
|
# dist_name = dist.metadata['Name'] # Distribution.name was not added until 3.10, and it returns this
|
|
@@ -419,13 +448,30 @@ class DistributionFinder:
|
|
|
419
448
|
|
|
420
449
|
def _dist_for_obj_main(self, obj) -> Distribution | None:
|
|
421
450
|
# Note: getmodule returns the module object (obj.__module__ only provides the name)
|
|
422
|
-
if (module := getmodule(obj)) is None
|
|
451
|
+
if (module := getmodule(obj)) is None:
|
|
423
452
|
return None
|
|
453
|
+
elif not module.__package__:
|
|
454
|
+
# This may occur for top-level scripts that are in a bin/ directory or similar, with a Command that was
|
|
455
|
+
# defined in that file instead of in the package that represents the library code for that project
|
|
456
|
+
try:
|
|
457
|
+
path = module.__loader__.path # noqa
|
|
458
|
+
except AttributeError:
|
|
459
|
+
return None
|
|
460
|
+
else:
|
|
461
|
+
return self._dist_for_main_loader_path(path)
|
|
424
462
|
|
|
425
463
|
# The package name may have a prefix like `lib` not included in top_level when interactive
|
|
426
464
|
for part in module.__package__.split('.'):
|
|
427
465
|
if (dist := self.dist_for_pkg(part)) is not None:
|
|
428
466
|
return dist
|
|
467
|
+
|
|
468
|
+
return None
|
|
469
|
+
|
|
470
|
+
def _dist_for_main_loader_path(self, path_str: str) -> Distribution | None:
|
|
471
|
+
path = Path(path_str).resolve()
|
|
472
|
+
for name, (dist, src_path) in self._editable_distributions.items():
|
|
473
|
+
if path.is_relative_to(src_path):
|
|
474
|
+
return dist
|
|
429
475
|
return None
|
|
430
476
|
|
|
431
477
|
def get_urls(self, dist: Distribution) -> dict[str, str]:
|
|
@@ -445,6 +491,25 @@ class DistributionFinder:
|
|
|
445
491
|
return urls
|
|
446
492
|
|
|
447
493
|
|
|
494
|
+
def _get_editable_path(dist: Distribution) -> Path | None:
|
|
495
|
+
if not (direct_url := dist.read_text('direct_url.json')): # read_text suppresses errors
|
|
496
|
+
return None
|
|
497
|
+
|
|
498
|
+
data = json.loads(direct_url)
|
|
499
|
+
# direct_url content: '{"dir_info": {"editable": true}, "url": "file:///C:/Users/..."}'
|
|
500
|
+
if not (url := data.get('url')) or not data.get('dir_info', {}).get('editable'):
|
|
501
|
+
return None # This is not expected
|
|
502
|
+
|
|
503
|
+
parsed = urlparse(url)
|
|
504
|
+
if parsed.scheme != 'file': # This is not expected
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
path = parsed.path
|
|
508
|
+
if WINDOWS and path.startswith('/') and ':/' in path[:4]: # The uri path has a leading / before the drive letter
|
|
509
|
+
path = path[1:]
|
|
510
|
+
return Path(path).resolve()
|
|
511
|
+
|
|
512
|
+
|
|
448
513
|
_dist_finder = DistributionFinder()
|
|
449
514
|
|
|
450
515
|
|
|
@@ -21,6 +21,7 @@ from .actions import Concatenate
|
|
|
21
21
|
from .base import BasePositional
|
|
22
22
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
24
|
+
from ..formatting.params import ChoiceMapHelpFormatter
|
|
24
25
|
from ..metadata import ProgramMetadata
|
|
25
26
|
|
|
26
27
|
__all__ = ['SubCommand', 'Action', 'Choice', 'ChoiceMap']
|
|
@@ -84,6 +85,7 @@ class ChoiceMap(BasePositional[str], Generic[T], actions=(Concatenate,)):
|
|
|
84
85
|
choices: dict[str, Choice[T]]
|
|
85
86
|
title: OptStr
|
|
86
87
|
description: OptStr
|
|
88
|
+
formatter: ChoiceMapHelpFormatter
|
|
87
89
|
|
|
88
90
|
def __init_subclass__( # pylint: disable=W0222
|
|
89
91
|
cls, title: str = None, choice_validation_exc: Type[Exception] = None, **kwargs
|
|
@@ -15,6 +15,7 @@ from .base import BaseOption, BasePositional, ParamBase, _group_stack
|
|
|
15
15
|
from .pass_thru import PassThru
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
|
+
from ..formatting.params import GroupHelpFormatter
|
|
18
19
|
from ..typing import Bool, ParamList, ParamOrGroup
|
|
19
20
|
|
|
20
21
|
__all__ = ['ParamGroup']
|
|
@@ -48,6 +49,7 @@ class ParamGroup(ParamBase):
|
|
|
48
49
|
members: list[ParamOrGroup]
|
|
49
50
|
mutually_exclusive: Bool = False
|
|
50
51
|
mutually_dependent: Bool = False
|
|
52
|
+
formatter: GroupHelpFormatter
|
|
51
53
|
|
|
52
54
|
def __init__(
|
|
53
55
|
self,
|
|
@@ -202,6 +204,11 @@ class ParamGroup(ParamBase):
|
|
|
202
204
|
if not (self.mutually_dependent or self.mutually_exclusive):
|
|
203
205
|
return
|
|
204
206
|
|
|
207
|
+
# TODO: Use case: partially mutually dependent group - outer mutually dependent group where within the group,
|
|
208
|
+
# some params are required if any members are provided, but not all members (which may be groups themselves)
|
|
209
|
+
# are always required. If those optional members are provided, then the required members must be required,
|
|
210
|
+
# but not the inverse.
|
|
211
|
+
|
|
205
212
|
# log.debug(f'{self}: Checking group conflicts in {provided=}, {missing=}')
|
|
206
213
|
# log.debug(f'{self}: Checking group conflicts in provided={len(provided)}, missing={len(missing)}')
|
|
207
214
|
if self.mutually_dependent and provided and missing:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cli_command_parser
|
|
3
|
-
Version: 2024.
|
|
3
|
+
Version: 2024.11.2
|
|
4
4
|
Summary: CLI Command Parser
|
|
5
5
|
Home-page: https://github.com/dskrypa/cli_command_parser
|
|
6
6
|
Author: Doug Skrypa
|
|
@@ -16,27 +16,25 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python
|
|
18
18
|
Classifier: Programming Language :: Python :: 3
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
24
|
Classifier: Topic :: Software Development :: User Interfaces
|
|
25
25
|
Classifier: Topic :: Text Processing
|
|
26
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
27
|
Description-Content-Type: text/x-rst
|
|
28
28
|
License-File: LICENSE
|
|
29
29
|
Provides-Extra: wcwidth
|
|
30
30
|
Requires-Dist: wcwidth; extra == "wcwidth"
|
|
31
|
-
Provides-Extra: conversion
|
|
32
|
-
Requires-Dist: astunparse; python_version < "3.9" and extra == "conversion"
|
|
33
31
|
|
|
34
32
|
CLI Command Parser
|
|
35
33
|
##################
|
|
36
34
|
|
|
37
35
|
|downloads| |py_version| |coverage_badge| |build_status| |Ruff|
|
|
38
36
|
|
|
39
|
-
.. |py_version| image:: https://img.shields.io/badge/python-3.
|
|
37
|
+
.. |py_version| image:: https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20-blue
|
|
40
38
|
:target: https://pypi.org/project/cli-command-parser/
|
|
41
39
|
|
|
42
40
|
.. |coverage_badge| image:: https://codecov.io/gh/dskrypa/cli_command_parser/branch/main/graph/badge.svg
|
|
@@ -118,18 +116,8 @@ with optional dependencies::
|
|
|
118
116
|
Python Version Compatibility
|
|
119
117
|
============================
|
|
120
118
|
|
|
121
|
-
Python versions 3.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
When using Python 3.8, some additional packages that backport functionality that was added in later Python versions
|
|
125
|
-
are required for compatibility.
|
|
126
|
-
|
|
127
|
-
To use the argparse to cli-command-parser conversion script with Python 3.8, there is a dependency on
|
|
128
|
-
`astunparse <https://astunparse.readthedocs.io>`__. If you are using Python 3.9 or above, then ``astunparse`` is not
|
|
129
|
-
necessary because the relevant code was added to the stdlib ``ast`` module. If you're unsure, you can install
|
|
130
|
-
cli-command-parser with the following command to automatically handle whether that extra dependency is needed or not::
|
|
131
|
-
|
|
132
|
-
$ pip install -U cli-command-parser[conversion]
|
|
119
|
+
Python versions 3.9 and above are currently supported. The last release of CLI Command Parser that supported 3.8 was
|
|
120
|
+
2024-09-07. Support for Python 3.8 `officially ended on 2024-10-07 <https://devguide.python.org/versions/>`__.
|
|
133
121
|
|
|
134
122
|
|
|
135
123
|
Links
|
|
@@ -3,7 +3,7 @@ CLI Command Parser
|
|
|
3
3
|
|
|
4
4
|
|downloads| |py_version| |coverage_badge| |build_status| |Ruff|
|
|
5
5
|
|
|
6
|
-
.. |py_version| image:: https://img.shields.io/badge/python-3.
|
|
6
|
+
.. |py_version| image:: https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20-blue
|
|
7
7
|
:target: https://pypi.org/project/cli-command-parser/
|
|
8
8
|
|
|
9
9
|
.. |coverage_badge| image:: https://codecov.io/gh/dskrypa/cli_command_parser/branch/main/graph/badge.svg
|
|
@@ -85,18 +85,8 @@ with optional dependencies::
|
|
|
85
85
|
Python Version Compatibility
|
|
86
86
|
============================
|
|
87
87
|
|
|
88
|
-
Python versions 3.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
When using Python 3.8, some additional packages that backport functionality that was added in later Python versions
|
|
92
|
-
are required for compatibility.
|
|
93
|
-
|
|
94
|
-
To use the argparse to cli-command-parser conversion script with Python 3.8, there is a dependency on
|
|
95
|
-
`astunparse <https://astunparse.readthedocs.io>`__. If you are using Python 3.9 or above, then ``astunparse`` is not
|
|
96
|
-
necessary because the relevant code was added to the stdlib ``ast`` module. If you're unsure, you can install
|
|
97
|
-
cli-command-parser with the following command to automatically handle whether that extra dependency is needed or not::
|
|
98
|
-
|
|
99
|
-
$ pip install -U cli-command-parser[conversion]
|
|
88
|
+
Python versions 3.9 and above are currently supported. The last release of CLI Command Parser that supported 3.8 was
|
|
89
|
+
2024-09-07. Support for Python 3.8 `officially ended on 2024-10-07 <https://devguide.python.org/versions/>`__.
|
|
100
90
|
|
|
101
91
|
|
|
102
92
|
Links
|
|
@@ -21,11 +21,11 @@ classifiers =
|
|
|
21
21
|
Operating System :: OS Independent
|
|
22
22
|
Programming Language :: Python
|
|
23
23
|
Programming Language :: Python :: 3
|
|
24
|
-
Programming Language :: Python :: 3.8
|
|
25
24
|
Programming Language :: Python :: 3.9
|
|
26
25
|
Programming Language :: Python :: 3.10
|
|
27
26
|
Programming Language :: Python :: 3.11
|
|
28
27
|
Programming Language :: Python :: 3.12
|
|
28
|
+
Programming Language :: Python :: 3.13
|
|
29
29
|
Topic :: Software Development :: User Interfaces
|
|
30
30
|
Topic :: Text Processing
|
|
31
31
|
|
|
@@ -34,7 +34,7 @@ include_package_data = True
|
|
|
34
34
|
entry_points = file: entry_points.txt
|
|
35
35
|
packages = find:
|
|
36
36
|
package_dir = = lib
|
|
37
|
-
python_requires = >=3.
|
|
37
|
+
python_requires = >=3.9
|
|
38
38
|
tests_require = testtools; coverage
|
|
39
39
|
|
|
40
40
|
[options.packages.find]
|
|
@@ -43,8 +43,6 @@ where = lib
|
|
|
43
43
|
[options.extras_require]
|
|
44
44
|
wcwidth =
|
|
45
45
|
wcwidth
|
|
46
|
-
conversion =
|
|
47
|
-
astunparse; python_version<"3.9"
|
|
48
46
|
|
|
49
47
|
[egg_info]
|
|
50
48
|
tag_build =
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/__init__.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/commands.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/config.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/context.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
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/files.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/time.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/inputs/utils.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/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
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parse_tree.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/parser.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/testing.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/lib/cli_command_parser/typing.py
RENAMED
|
File without changes
|
{cli_command_parser-2024.8.17 → cli_command_parser-2024.11.2}/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
|