cli-command-parser 2025.5.10__tar.gz → 2025.6.14__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-2025.5.10/lib/cli_command_parser.egg-info → cli_command_parser-2025.6.14}/PKG-INFO +1 -1
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/__init__.py +25 -25
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/__version__.py +1 -1
- cli_command_parser-2025.6.14/lib/cli_command_parser/conversion/__init__.py +12 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/cli.py +1 -1
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/documentation.py +17 -1
- cli_command_parser-2025.6.14/lib/cli_command_parser/error_handling/__init__.py +38 -0
- cli_command_parser-2025.5.10/lib/cli_command_parser/error_handling.py → cli_command_parser-2025.6.14/lib/cli_command_parser/error_handling/base.py +15 -68
- cli_command_parser-2025.6.14/lib/cli_command_parser/error_handling/other.py +20 -0
- cli_command_parser-2025.6.14/lib/cli_command_parser/error_handling/windows.py +55 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/formatting/params.py +3 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/__init__.py +8 -8
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/choices.py +2 -2
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/exceptions.py +1 -1
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/time.py +77 -72
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/utils.py +2 -2
- cli_command_parser-2025.6.14/lib/cli_command_parser/parameters/__init__.py +6 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/options.py +9 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/positionals.py +3 -3
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/testing.py +10 -10
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14/lib/cli_command_parser.egg-info}/PKG-INFO +1 -1
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser.egg-info/SOURCES.txt +4 -1
- cli_command_parser-2025.5.10/lib/cli_command_parser/conversion/__init__.py +0 -3
- cli_command_parser-2025.5.10/lib/cli_command_parser/parameters/__init__.py +0 -6
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/LICENSE +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/MANIFEST.in +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/entry_points.txt +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/__main__.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/annotations.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/command_parameters.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/commands.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/compat.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/config.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/context.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/__main__.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/argparse_ast.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/argparse_utils.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/command_builder.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/utils.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/conversion/visitor.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/core.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/exceptions.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/formatting/__init__.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/formatting/commands.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/formatting/restructured_text.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/formatting/utils.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/base.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/files.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/numeric.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/patterns.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/metadata.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/nargs.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/actions.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/base.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/choice_map.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/groups.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/option_strings.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parameters/pass_thru.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parse_tree.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parser.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/typing.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/utils.py +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser.egg-info/dependency_links.txt +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser.egg-info/entry_points.txt +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser.egg-info/requires.txt +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser.egg-info/top_level.txt +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/pyproject.toml +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/readme.rst +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/requirements-dev.txt +0 -0
- {cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/setup.cfg +0 -0
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/__init__.py
RENAMED
|
@@ -4,52 +4,52 @@ Command Parser
|
|
|
4
4
|
:author: Doug Skrypa
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from .commands import AsyncCommand, Command, main
|
|
7
8
|
from .config import (
|
|
9
|
+
AllowLeadingDash,
|
|
10
|
+
AmbiguousComboMode,
|
|
8
11
|
CommandConfig,
|
|
9
|
-
ShowDefaults,
|
|
10
12
|
OptionNameMode,
|
|
13
|
+
ShowDefaults,
|
|
11
14
|
SubcommandAliasHelpMode,
|
|
12
|
-
AmbiguousComboMode,
|
|
13
|
-
AllowLeadingDash,
|
|
14
15
|
)
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
16
|
+
from .context import Context, ctx, get_context, get_current_context, get_parsed, get_raw_arg
|
|
17
|
+
from .error_handling import ErrorHandler, error_handler, extended_error_handler, no_exit_handler
|
|
17
18
|
from .exceptions import (
|
|
18
|
-
|
|
19
|
-
CommandDefinitionError,
|
|
20
|
-
ParameterDefinitionError,
|
|
21
|
-
UsageError,
|
|
22
|
-
ParamUsageError,
|
|
19
|
+
AmbiguousParseTree,
|
|
23
20
|
BadArgument,
|
|
21
|
+
CommandDefinitionError,
|
|
22
|
+
CommandParserException,
|
|
24
23
|
InvalidChoice,
|
|
25
24
|
MissingArgument,
|
|
26
|
-
|
|
25
|
+
NoActiveContext,
|
|
27
26
|
NoSuchOption,
|
|
28
|
-
ParserExit,
|
|
29
27
|
ParamConflict,
|
|
28
|
+
ParameterDefinitionError,
|
|
30
29
|
ParamsMissing,
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
ParamUsageError,
|
|
31
|
+
ParserExit,
|
|
32
|
+
TooManyArguments,
|
|
33
|
+
UsageError,
|
|
33
34
|
)
|
|
34
|
-
from .error_handling import ErrorHandler, error_handler, no_exit_handler, extended_error_handler
|
|
35
35
|
from .formatting.commands import get_formatter
|
|
36
36
|
from .nargs import REMAINDER
|
|
37
37
|
from .parameters import (
|
|
38
|
+
Action,
|
|
39
|
+
ActionFlag,
|
|
40
|
+
BaseOption,
|
|
41
|
+
BasePositional,
|
|
42
|
+
Counter,
|
|
43
|
+
Flag,
|
|
44
|
+
Option,
|
|
38
45
|
Parameter,
|
|
46
|
+
ParamGroup,
|
|
39
47
|
PassThru,
|
|
40
|
-
BasePositional,
|
|
41
48
|
Positional,
|
|
42
49
|
SubCommand,
|
|
43
|
-
|
|
44
|
-
BaseOption,
|
|
45
|
-
Option,
|
|
46
|
-
Flag,
|
|
47
|
-
Counter,
|
|
48
|
-
ActionFlag,
|
|
50
|
+
TriFlag,
|
|
49
51
|
action_flag,
|
|
50
|
-
before_main,
|
|
51
52
|
after_main,
|
|
52
|
-
|
|
53
|
-
TriFlag,
|
|
53
|
+
before_main,
|
|
54
54
|
)
|
|
55
55
|
from .typing import Param, ParamOrGroup
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/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__ = '2025.
|
|
4
|
+
__version__ = '2025.06.14'
|
|
5
5
|
__author__ = 'Doug Skrypa'
|
|
6
6
|
__author_email__ = 'dskrypa@gmail.com'
|
|
7
7
|
__license__ = 'Apache 2.0'
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
from functools import cached_property
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from cli_command_parser import Command, Counter,
|
|
7
|
+
from cli_command_parser import Command, Counter, Flag, ParamGroup, Positional, SubCommand, main
|
|
8
8
|
from cli_command_parser.inputs import Path as IPath
|
|
9
9
|
|
|
10
10
|
log = logging.getLogger(__name__)
|
|
@@ -132,15 +132,17 @@ def top_level_commands(commands: Commands) -> Commands:
|
|
|
132
132
|
def import_module(path: PathLike):
|
|
133
133
|
"""Import the module / package from the given path"""
|
|
134
134
|
path = Path(path)
|
|
135
|
-
name = path
|
|
135
|
+
name = _module_name(path)
|
|
136
136
|
if path.is_dir():
|
|
137
137
|
path /= '__init__.py'
|
|
138
|
+
|
|
138
139
|
spec = spec_from_file_location(name, path)
|
|
139
140
|
try:
|
|
140
141
|
module = module_from_spec(spec)
|
|
141
142
|
except AttributeError as e:
|
|
142
143
|
path_str = path.as_posix()
|
|
143
144
|
raise ImportError(f'Invalid path={path_str!r} - are you sure it is a Python module?', path=path_str) from e
|
|
145
|
+
|
|
144
146
|
sys.modules[spec.name] = module # This is required for the program metadata introspection
|
|
145
147
|
try:
|
|
146
148
|
spec.loader.exec_module(module)
|
|
@@ -150,6 +152,20 @@ def import_module(path: PathLike):
|
|
|
150
152
|
return module
|
|
151
153
|
|
|
152
154
|
|
|
155
|
+
def _module_name(path: Path) -> str:
|
|
156
|
+
if path.name == '__init__.py':
|
|
157
|
+
path = path.parent
|
|
158
|
+
|
|
159
|
+
parts = [path.stem]
|
|
160
|
+
while (path := path.parent).name: # / has no name
|
|
161
|
+
if path.joinpath('__init__.py').exists(): # it is a package
|
|
162
|
+
parts.append(path.name)
|
|
163
|
+
else:
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
return '.'.join(parts[::-1])
|
|
167
|
+
|
|
168
|
+
|
|
153
169
|
def _is_command(obj, include_abc: Bool = False) -> bool:
|
|
154
170
|
if not (isinstance(obj, CommandMeta) and obj is not Command):
|
|
155
171
|
return False
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error handling for expected / unexpected exceptions.
|
|
3
|
+
|
|
4
|
+
The default handler will...
|
|
5
|
+
|
|
6
|
+
- Call ``print()`` after catching a :class:`python:KeyboardInterrupt`, before exiting
|
|
7
|
+
- Exit gracefully after catching a :class:`python:BrokenPipeError` (often caused by piping output to a tool like
|
|
8
|
+
``tail``)
|
|
9
|
+
|
|
10
|
+
.. note::
|
|
11
|
+
Parameters defined in a base Command will be processed in the context of that Command. I.e., if a valid
|
|
12
|
+
subcommand argument was provided, but an Option defined in the parent Command has an invalid value, then the
|
|
13
|
+
exception that is raised about that invalid value will be raised before transferring control to the
|
|
14
|
+
subcommand's error handler.
|
|
15
|
+
|
|
16
|
+
:author: Doug Skrypa
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from platform import system as _system
|
|
20
|
+
|
|
21
|
+
from .base import (
|
|
22
|
+
ErrorHandler,
|
|
23
|
+
Handler,
|
|
24
|
+
HandlerFunc,
|
|
25
|
+
NullErrorHandler,
|
|
26
|
+
error_handler,
|
|
27
|
+
extended_error_handler,
|
|
28
|
+
no_exit_handler,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if _system().lower() == 'windows':
|
|
32
|
+
from .windows import handle_kb_interrupt
|
|
33
|
+
else:
|
|
34
|
+
from .other import handle_kb_interrupt
|
|
35
|
+
|
|
36
|
+
__all__ = ['ErrorHandler', 'Handler', 'error_handler', 'extended_error_handler', 'no_exit_handler', 'NullErrorHandler']
|
|
37
|
+
|
|
38
|
+
error_handler.register(handle_kb_interrupt, KeyboardInterrupt)
|
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
The default handler will...
|
|
5
|
-
|
|
6
|
-
- Call ``print()`` after catching a :class:`python:KeyboardInterrupt`, before exiting
|
|
7
|
-
- Exit gracefully after catching a :class:`python:BrokenPipeError` (often caused by piping output to a tool like
|
|
8
|
-
``tail``)
|
|
9
|
-
|
|
10
|
-
.. note::
|
|
11
|
-
Parameters defined in a base Command will be processed in the context of that Command. I.e., if a valid
|
|
12
|
-
subcommand argument was provided, but an Option defined in the parent Command has an invalid value, then the
|
|
13
|
-
exception that is raised about that invalid value will be raised before transferring control to the
|
|
14
|
-
subcommand's error handler.
|
|
15
|
-
|
|
16
|
-
:author: Doug Skrypa
|
|
2
|
+
Platform-agnostic error handling framework / handlers.
|
|
17
3
|
"""
|
|
18
4
|
|
|
19
5
|
from __future__ import annotations
|
|
20
6
|
|
|
21
|
-
import platform
|
|
22
7
|
import sys
|
|
23
8
|
from collections import ChainMap
|
|
24
|
-
from typing import Callable, Iterator,
|
|
9
|
+
from typing import Callable, Iterator, Type, TypeVar, Union
|
|
25
10
|
|
|
26
|
-
from
|
|
11
|
+
from ..exceptions import CommandParserException
|
|
27
12
|
|
|
28
13
|
__all__ = ['ErrorHandler', 'error_handler', 'extended_error_handler', 'no_exit_handler', 'NullErrorHandler']
|
|
29
14
|
|
|
30
|
-
|
|
31
|
-
HandlerFunc = Callable[[
|
|
15
|
+
E = TypeVar('E', bound=BaseException)
|
|
16
|
+
HandlerFunc = Callable[[E], Union[bool, int, None]]
|
|
32
17
|
|
|
33
18
|
|
|
34
19
|
class ErrorHandler:
|
|
@@ -42,7 +27,7 @@ class ErrorHandler:
|
|
|
42
27
|
def __repr__(self) -> str:
|
|
43
28
|
return f'<{self.__class__.__name__}[handlers={len(self.exc_handler_map)}]>'
|
|
44
29
|
|
|
45
|
-
def register(self, handler: HandlerFunc, *exceptions: Type[
|
|
30
|
+
def register(self, handler: HandlerFunc, *exceptions: Type[E]):
|
|
46
31
|
for exc in exceptions:
|
|
47
32
|
self.exc_handler_map[exc] = Handler(exc, handler)
|
|
48
33
|
|
|
@@ -61,7 +46,7 @@ class ErrorHandler:
|
|
|
61
46
|
return _handler
|
|
62
47
|
|
|
63
48
|
@classmethod
|
|
64
|
-
def cls_handler(cls, *exceptions: Type[
|
|
49
|
+
def cls_handler(cls, *exceptions: Type[E]):
|
|
65
50
|
def _cls_handler(handler: Union[HandlerFunc, staticmethod]):
|
|
66
51
|
for exc in exceptions:
|
|
67
52
|
cls._exc_handler_map[exc] = Handler(exc, handler)
|
|
@@ -91,6 +76,7 @@ class ErrorHandler:
|
|
|
91
76
|
for handler in self.iter_handlers(exc_type, exc_val):
|
|
92
77
|
result = handler(exc_val)
|
|
93
78
|
if result is True:
|
|
79
|
+
# This explicitly checks for True since 1 == True, but 1 is treated as an intended exit code
|
|
94
80
|
return True
|
|
95
81
|
if result or (isinstance(result, int) and result is not False):
|
|
96
82
|
sys.exit(result)
|
|
@@ -130,62 +116,23 @@ class Handler:
|
|
|
130
116
|
return issubclass(self.exc_cls, other.exc_cls)
|
|
131
117
|
|
|
132
118
|
|
|
133
|
-
|
|
119
|
+
# By default, all error handlers should call :meth:`CommandParserException.exit` for CommandParserExceptions
|
|
120
|
+
ErrorHandler.cls_handler(CommandParserException)(CommandParserException.exit) # noqa
|
|
134
121
|
|
|
135
122
|
#: Default base :class:`ErrorHandler`
|
|
136
123
|
error_handler: ErrorHandler = ErrorHandler()
|
|
137
|
-
error_handler.register(lambda e: True, BrokenPipeError)
|
|
138
124
|
|
|
139
125
|
|
|
140
|
-
@error_handler(
|
|
141
|
-
def
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
causes the next terminal prompt to be printed on the same line as the last (possibly incomplete) line of output.
|
|
145
|
-
"""
|
|
146
|
-
try:
|
|
147
|
-
print(flush=True) # Flush forces any potential closed/broken pipe-related error to be caught/handled here
|
|
148
|
-
except BrokenPipeError:
|
|
149
|
-
pass
|
|
150
|
-
except OSError as e:
|
|
151
|
-
# Handle the closed/broken pipe incorrect errno bug if triggered during the above print
|
|
152
|
-
if not WINDOWS or not handle_win_os_pipe_error(e):
|
|
153
|
-
raise
|
|
154
|
-
return 130
|
|
126
|
+
@error_handler(BrokenPipeError)
|
|
127
|
+
def _handle_broken_pipe(exc: BrokenPipeError):
|
|
128
|
+
# This can't be registered as a lambda function because it would break the ability to pickle the handler
|
|
129
|
+
return True
|
|
155
130
|
|
|
156
131
|
|
|
157
132
|
#: An :class:`ErrorHandler` that does not call :func:`python:sys.exit` for
|
|
158
133
|
#: :class:`CommandParserExceptions<.CommandParserException>`
|
|
159
134
|
no_exit_handler: ErrorHandler = error_handler.copy()
|
|
160
|
-
no_exit_handler(CommandParserException
|
|
135
|
+
no_exit_handler.register(CommandParserException.show, CommandParserException) # noqa
|
|
161
136
|
|
|
162
137
|
#: The default :class:`ErrorHandler` (extends :obj:`error_handler`)
|
|
163
138
|
extended_error_handler: ErrorHandler = error_handler.copy()
|
|
164
|
-
|
|
165
|
-
if WINDOWS:
|
|
166
|
-
import ctypes
|
|
167
|
-
|
|
168
|
-
RtlGetLastNtStatus = ctypes.WinDLL('ntdll').RtlGetLastNtStatus
|
|
169
|
-
RtlGetLastNtStatus.restype = ctypes.c_ulong
|
|
170
|
-
NT_STATUSES = {0xC000_00B1: 'STATUS_PIPE_CLOSING', 0xC000_014B: 'STATUS_PIPE_BROKEN'}
|
|
171
|
-
|
|
172
|
-
@extended_error_handler(OSError)
|
|
173
|
-
def handle_win_os_pipe_error(exc: OSError):
|
|
174
|
-
"""
|
|
175
|
-
This is a workaround for `[Windows] I/O on a broken pipe may raise an EINVAL OSError instead of BrokenPipeError
|
|
176
|
-
<https://github.com/python/cpython/issues/79935>`_, which is a bug in the way that the
|
|
177
|
-
windows error code for a broken pipe is translated into an errno value. It should be translated to
|
|
178
|
-
:data:`~errno.EPIPE`, but it uses :data:`~errno.EINVAL` (22) instead.
|
|
179
|
-
|
|
180
|
-
Prevents the following when piping output to utilities such as ``| head``::\n
|
|
181
|
-
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
|
|
182
|
-
OSError: [Errno 22] Invalid argument
|
|
183
|
-
"""
|
|
184
|
-
if exc.errno == 22 and RtlGetLastNtStatus() in NT_STATUSES:
|
|
185
|
-
try:
|
|
186
|
-
sys.stdout.close()
|
|
187
|
-
except OSError:
|
|
188
|
-
pass
|
|
189
|
-
return True
|
|
190
|
-
|
|
191
|
-
return False
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error handling for expected / unexpected exceptions on non-Windows systems.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
__all__ = ['handle_kb_interrupt']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def handle_kb_interrupt(exc: KeyboardInterrupt) -> int:
|
|
11
|
+
"""
|
|
12
|
+
Handles :class:`python:KeyboardInterrupt` by calling :func:`python:print` to avoid ending the program in a way that
|
|
13
|
+
causes the next terminal prompt to be printed on the same line as the last (possibly incomplete) line of output.
|
|
14
|
+
"""
|
|
15
|
+
try:
|
|
16
|
+
print(flush=True) # Flush forces any potential closed/broken pipe-related error to be caught/handled here
|
|
17
|
+
except BrokenPipeError:
|
|
18
|
+
pass
|
|
19
|
+
# 130 (= 128 + SIGINT (2)) is used/expected by Bash; see: https://tldp.org/LDP/abs/html/exitcodes.html
|
|
20
|
+
return 130
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Error handling for expected / unexpected exceptions on Windows systems.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import ctypes
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from .base import extended_error_handler
|
|
11
|
+
|
|
12
|
+
__all__ = ['handle_kb_interrupt']
|
|
13
|
+
|
|
14
|
+
RtlGetLastNtStatus = ctypes.WinDLL('ntdll').RtlGetLastNtStatus
|
|
15
|
+
RtlGetLastNtStatus.restype = ctypes.c_ulong
|
|
16
|
+
NT_STATUSES = {0xC000_00B1: 'STATUS_PIPE_CLOSING', 0xC000_014B: 'STATUS_PIPE_BROKEN'}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def handle_kb_interrupt(exc: KeyboardInterrupt) -> int:
|
|
20
|
+
"""
|
|
21
|
+
Handles :class:`python:KeyboardInterrupt` by calling :func:`python:print` to avoid ending the program in a way that
|
|
22
|
+
causes the next terminal prompt to be printed on the same line as the last (possibly incomplete) line of output.
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
print(flush=True) # Flush forces any potential closed/broken pipe-related error to be caught/handled here
|
|
26
|
+
except BrokenPipeError:
|
|
27
|
+
pass
|
|
28
|
+
except OSError as e:
|
|
29
|
+
# Handle the closed/broken pipe incorrect errno bug if triggered during the above print
|
|
30
|
+
if not handle_win_os_pipe_error(e):
|
|
31
|
+
raise
|
|
32
|
+
return 130
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@extended_error_handler(OSError)
|
|
36
|
+
def handle_win_os_pipe_error(exc: OSError):
|
|
37
|
+
"""
|
|
38
|
+
This is a workaround for `[Windows] I/O on a broken pipe may raise an EINVAL OSError instead of BrokenPipeError
|
|
39
|
+
<https://github.com/python/cpython/issues/79935>`_, which is a bug in the way that the
|
|
40
|
+
Windows error code for a broken pipe is translated into an errno value. It should be translated to
|
|
41
|
+
:data:`~errno.EPIPE`, but it uses :data:`~errno.EINVAL` (22) instead.
|
|
42
|
+
|
|
43
|
+
Prevents the following when piping output to utilities such as ``| head``::
|
|
44
|
+
|
|
45
|
+
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
|
|
46
|
+
OSError: [Errno 22] Invalid argument
|
|
47
|
+
"""
|
|
48
|
+
if exc.errno == 22 and RtlGetLastNtStatus() in NT_STATUSES:
|
|
49
|
+
try:
|
|
50
|
+
sys.stdout.close()
|
|
51
|
+
except OSError:
|
|
52
|
+
pass
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
return False
|
|
@@ -56,6 +56,9 @@ class ParamHelpFormatter:
|
|
|
56
56
|
def __init__(self, param: ParamOrGroup):
|
|
57
57
|
self.param = param
|
|
58
58
|
|
|
59
|
+
def __getnewargs__(self):
|
|
60
|
+
return (self.param,)
|
|
61
|
+
|
|
59
62
|
def maybe_wrap_usage(self, text: str) -> str:
|
|
60
63
|
"""
|
|
61
64
|
Wraps the provided text in parentheses / brackets / etc based on whether the associated Parameter is required,
|
|
@@ -10,17 +10,17 @@ import typing as _t
|
|
|
10
10
|
from enum import Enum as _Enum
|
|
11
11
|
|
|
12
12
|
from ..exceptions import ParameterDefinitionError as _ParameterDefinitionError
|
|
13
|
-
from .exceptions import InputValidationError, InvalidChoiceError
|
|
14
|
-
from .utils import StatMode, FileWrapper
|
|
15
13
|
from .base import InputType
|
|
16
|
-
from .choices import
|
|
17
|
-
from .
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
20
|
-
from .
|
|
14
|
+
from .choices import ChoiceMap, Choices, EnumChoices
|
|
15
|
+
from .exceptions import InputValidationError, InvalidChoiceError
|
|
16
|
+
from .files import File, Json, Path, Pickle, Serialized
|
|
17
|
+
from .numeric import NumRange, Range
|
|
18
|
+
from .patterns import Glob, Regex, RegexMode
|
|
19
|
+
from .time import Date, DateTime, Day, DTFormatMode, Month, Time, TimeDelta
|
|
20
|
+
from .utils import FileWrapper, StatMode
|
|
21
21
|
|
|
22
22
|
if _t.TYPE_CHECKING:
|
|
23
|
-
from ..typing import
|
|
23
|
+
from ..typing import ChoicesType, InputTypeFunc, TypeFunc
|
|
24
24
|
|
|
25
25
|
# fmt: off
|
|
26
26
|
__all__ = [
|
|
@@ -8,9 +8,9 @@ from __future__ import annotations
|
|
|
8
8
|
|
|
9
9
|
from abc import ABC, abstractmethod
|
|
10
10
|
from enum import Enum
|
|
11
|
-
from typing import TYPE_CHECKING, Any,
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Collection, Iterator, Mapping, Optional, Set, Type, TypeVar
|
|
12
12
|
|
|
13
|
-
from ..typing import
|
|
13
|
+
from ..typing import T, TypeFunc
|
|
14
14
|
from .base import InputType
|
|
15
15
|
from .exceptions import InvalidChoiceError
|
|
16
16
|
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/time.py
RENAMED
|
@@ -112,6 +112,18 @@ class DTFormatMode(MissingMixin, Enum):
|
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
class CalendarUnitInput(DTInput[Union[str, int]], ABC):
|
|
115
|
+
"""
|
|
116
|
+
Input type representing a date/time unit.
|
|
117
|
+
|
|
118
|
+
:param full: Allow the full unit name to be provided
|
|
119
|
+
:param abbreviation: Allow abbreviations of unit names to be provided
|
|
120
|
+
:param numeric: Allow unit values to be specified as a decimal number
|
|
121
|
+
:param locale: An alternate locale to use when parsing input
|
|
122
|
+
:param out_format: A :class:`DTFormatMode` or str that matches a format mode. Defaults to full weekday name.
|
|
123
|
+
:param out_locale: Alternate locale to use for output. Defaults to the same value as ``locale``.
|
|
124
|
+
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
125
|
+
"""
|
|
126
|
+
|
|
115
127
|
__slots__ = ('full', 'abbreviation', 'numeric', 'out_format', 'out_locale')
|
|
116
128
|
_formats: dict[DTFormatMode, Sequence[Union[str, int]]]
|
|
117
129
|
_min_index: int = 0
|
|
@@ -131,17 +143,6 @@ class CalendarUnitInput(DTInput[Union[str, int]], ABC):
|
|
|
131
143
|
out_locale: Locale = None,
|
|
132
144
|
fix_default: Bool = True,
|
|
133
145
|
):
|
|
134
|
-
"""
|
|
135
|
-
Input type representing a date/time unit.
|
|
136
|
-
|
|
137
|
-
:param full: Allow the full unit name to be provided
|
|
138
|
-
:param abbreviation: Allow abbreviations of unit names to be provided
|
|
139
|
-
:param numeric: Allow unit values to be specified as a decimal number
|
|
140
|
-
:param locale: An alternate locale to use when parsing input
|
|
141
|
-
:param out_format: A :class:`DTFormatMode` or str that matches a format mode. Defaults to full weekday name.
|
|
142
|
-
:param out_locale: Alternate locale to use for output. Defaults to the same value as ``locale``.
|
|
143
|
-
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
144
|
-
"""
|
|
145
146
|
if not (full or abbreviation or numeric):
|
|
146
147
|
raise ValueError('At least one of full, abbreviation, or numeric must be True')
|
|
147
148
|
super().__init__(locale=locale, fix_default=fix_default)
|
|
@@ -224,6 +225,20 @@ class CalendarUnitInput(DTInput[Union[str, int]], ABC):
|
|
|
224
225
|
|
|
225
226
|
|
|
226
227
|
class Day(CalendarUnitInput, dt_type='day of the week'):
|
|
228
|
+
"""
|
|
229
|
+
Input type representing a day of the week.
|
|
230
|
+
|
|
231
|
+
:param full: Allow the full day name to be provided
|
|
232
|
+
:param abbreviation: Allow abbreviations of day names to be provided
|
|
233
|
+
:param numeric: Allow weekdays to be specified as a decimal number
|
|
234
|
+
:param iso: Ignored if ``numeric`` is False. If True, then numeric weekdays are treated as ISO 8601 weekdays,
|
|
235
|
+
where 1 is Monday and 7 is Sunday. If False, then 0 is Monday and 6 is Sunday.
|
|
236
|
+
:param locale: An alternate locale to use when parsing input
|
|
237
|
+
:param out_format: A :class:`DTFormatMode` or str that matches a format mode. Defaults to full weekday name.
|
|
238
|
+
:param out_locale: Alternate locale to use for output. Defaults to the same value as ``locale``.
|
|
239
|
+
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
240
|
+
"""
|
|
241
|
+
|
|
227
242
|
__slots__ = ('iso',)
|
|
228
243
|
_formats = {
|
|
229
244
|
DTFormatMode.FULL: day_name,
|
|
@@ -244,21 +259,7 @@ class Day(CalendarUnitInput, dt_type='day of the week'):
|
|
|
244
259
|
out_format: Union[str, DTFormatMode] = DTFormatMode.FULL,
|
|
245
260
|
out_locale: Locale = None,
|
|
246
261
|
fix_default: Bool = True,
|
|
247
|
-
):
|
|
248
|
-
"""
|
|
249
|
-
Input type representing a day of the week.
|
|
250
|
-
|
|
251
|
-
:param full: Allow the full day name to be provided
|
|
252
|
-
:param abbreviation: Allow abbreviations of day names to be provided
|
|
253
|
-
:param numeric: Allow weekdays to be specified as a decimal number
|
|
254
|
-
:param iso: Ignored if ``numeric`` is False. If True, then numeric weekdays are treated as ISO 8601 weekdays,
|
|
255
|
-
where 1 is Monday and 7 is Sunday. If False, then 0 is Monday and 6 is Sunday.
|
|
256
|
-
:param locale: An alternate locale to use when parsing input
|
|
257
|
-
:param out_format: A :class:`DTFormatMode` or str that matches a format mode. Defaults to full weekday name.
|
|
258
|
-
:param out_locale: Alternate locale to use for output. Defaults to the same value as ``locale``.
|
|
259
|
-
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
260
|
-
"""
|
|
261
|
-
...
|
|
262
|
+
): ...
|
|
262
263
|
|
|
263
264
|
def __init__(self, *, iso: Bool = False, **kwargs):
|
|
264
265
|
super().__init__(**kwargs)
|
|
@@ -283,6 +284,18 @@ class Day(CalendarUnitInput, dt_type='day of the week'):
|
|
|
283
284
|
|
|
284
285
|
|
|
285
286
|
class Month(CalendarUnitInput, dt_type='month', min_index=1):
|
|
287
|
+
"""
|
|
288
|
+
Input type representing a month.
|
|
289
|
+
|
|
290
|
+
:param full: Allow the full month name to be provided
|
|
291
|
+
:param abbreviation: Allow abbreviations of month names to be provided
|
|
292
|
+
:param numeric: Allow months to be specified as a decimal number
|
|
293
|
+
:param locale: An alternate locale to use when parsing input
|
|
294
|
+
:param out_format: A :class:`DTFormatMode` or str that matches a format mode. Defaults to full month name.
|
|
295
|
+
:param out_locale: Alternate locale to use for output. Defaults to the same value as ``locale``.
|
|
296
|
+
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
297
|
+
"""
|
|
298
|
+
|
|
286
299
|
__slots__ = ()
|
|
287
300
|
_formats = {
|
|
288
301
|
DTFormatMode.FULL: month_name,
|
|
@@ -301,19 +314,7 @@ class Month(CalendarUnitInput, dt_type='month', min_index=1):
|
|
|
301
314
|
out_format: Union[str, DTFormatMode] = DTFormatMode.FULL,
|
|
302
315
|
out_locale: Locale = None,
|
|
303
316
|
fix_default: Bool = True,
|
|
304
|
-
):
|
|
305
|
-
"""
|
|
306
|
-
Input type representing a month.
|
|
307
|
-
|
|
308
|
-
:param full: Allow the full month name to be provided
|
|
309
|
-
:param abbreviation: Allow abbreviations of month names to be provided
|
|
310
|
-
:param numeric: Allow months to be specified as a decimal number
|
|
311
|
-
:param locale: An alternate locale to use when parsing input
|
|
312
|
-
:param out_format: A :class:`DTFormatMode` or str that matches a format mode. Defaults to full month name.
|
|
313
|
-
:param out_locale: Alternate locale to use for output. Defaults to the same value as ``locale``.
|
|
314
|
-
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
315
|
-
"""
|
|
316
|
-
...
|
|
317
|
+
): ...
|
|
317
318
|
|
|
318
319
|
def __init__(self, *, numeric: Bool = True, **kwargs):
|
|
319
320
|
super().__init__(numeric=numeric, **kwargs)
|
|
@@ -403,6 +404,7 @@ class DateTimeInput(DTInput[DT], ABC):
|
|
|
403
404
|
_type: Type[DT]
|
|
404
405
|
_earliest: TimeBound = None
|
|
405
406
|
_latest: TimeBound = None
|
|
407
|
+
# TODO: Add usage examples to the more user-friendly docs
|
|
406
408
|
|
|
407
409
|
def __init_subclass__(cls, type: Type[DT], **kwargs): # noqa
|
|
408
410
|
super().__init_subclass__(dt_type=type.__name__, **kwargs)
|
|
@@ -501,6 +503,18 @@ class DateTimeInput(DTInput[DT], ABC):
|
|
|
501
503
|
|
|
502
504
|
|
|
503
505
|
class DateTime(DateTimeInput[datetime], type=datetime):
|
|
506
|
+
"""
|
|
507
|
+
Input type that accepts any number of datetime format strings for parsing input. Parsing results in returning
|
|
508
|
+
a :class:`python:datetime.datetime` object.
|
|
509
|
+
|
|
510
|
+
:param formats: One or more :ref:`datetime format strings <python:strftime-strptime-behavior>`. Defaults to
|
|
511
|
+
:data:`DEFAULT_DT_FMT`.
|
|
512
|
+
:param locale: An alternate locale to use when parsing input
|
|
513
|
+
:param earliest: If specified, the parsed value must be later than or equal to this
|
|
514
|
+
:param latest: If specified, the parsed value must be earlier than or equal to this
|
|
515
|
+
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
516
|
+
"""
|
|
517
|
+
|
|
504
518
|
def __init__(
|
|
505
519
|
self,
|
|
506
520
|
*formats: str,
|
|
@@ -509,23 +523,24 @@ class DateTime(DateTimeInput[datetime], type=datetime):
|
|
|
509
523
|
latest: TimeBound = None,
|
|
510
524
|
fix_default: Bool = True,
|
|
511
525
|
):
|
|
512
|
-
"""
|
|
513
|
-
Input type that accepts any number of datetime format strings for parsing input. Parsing results in returning
|
|
514
|
-
a :class:`python:datetime.datetime` object.
|
|
515
|
-
|
|
516
|
-
:param formats: One or more :ref:`datetime format strings <python:strftime-strptime-behavior>`. Defaults to
|
|
517
|
-
:data:`DEFAULT_DT_FMT`.
|
|
518
|
-
:param locale: An alternate locale to use when parsing input
|
|
519
|
-
:param earliest: If specified, the parsed value must be later than or equal to this
|
|
520
|
-
:param latest: If specified, the parsed value must be earlier than or equal to this
|
|
521
|
-
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
522
|
-
"""
|
|
523
526
|
super().__init__(
|
|
524
527
|
formats or (DEFAULT_DT_FMT,), locale=locale, earliest=earliest, latest=latest, fix_default=fix_default
|
|
525
528
|
)
|
|
526
529
|
|
|
527
530
|
|
|
528
531
|
class Date(DateTimeInput[date], type=date):
|
|
532
|
+
"""
|
|
533
|
+
Input type that accepts any number of datetime format strings for parsing input. Parsing results in returning
|
|
534
|
+
a :class:`python:datetime.date` object.
|
|
535
|
+
|
|
536
|
+
:param formats: One or more :ref:`datetime format strings <python:strftime-strptime-behavior>`. Defaults to
|
|
537
|
+
:data:`DEFAULT_DT_FMT`.
|
|
538
|
+
:param locale: An alternate locale to use when parsing input
|
|
539
|
+
:param earliest: If specified, the parsed value must be later than or equal to this
|
|
540
|
+
:param latest: If specified, the parsed value must be earlier than or equal to this
|
|
541
|
+
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
542
|
+
"""
|
|
543
|
+
|
|
529
544
|
def __init__(
|
|
530
545
|
self,
|
|
531
546
|
*formats: str,
|
|
@@ -534,23 +549,24 @@ class Date(DateTimeInput[date], type=date):
|
|
|
534
549
|
latest: TimeBound = None,
|
|
535
550
|
fix_default: Bool = True,
|
|
536
551
|
):
|
|
537
|
-
"""
|
|
538
|
-
Input type that accepts any number of datetime format strings for parsing input. Parsing results in returning
|
|
539
|
-
a :class:`python:datetime.date` object.
|
|
540
|
-
|
|
541
|
-
:param formats: One or more :ref:`datetime format strings <python:strftime-strptime-behavior>`. Defaults to
|
|
542
|
-
:data:`DEFAULT_DT_FMT`.
|
|
543
|
-
:param locale: An alternate locale to use when parsing input
|
|
544
|
-
:param earliest: If specified, the parsed value must be later than or equal to this
|
|
545
|
-
:param latest: If specified, the parsed value must be earlier than or equal to this
|
|
546
|
-
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
547
|
-
"""
|
|
548
552
|
super().__init__(
|
|
549
553
|
formats or (DEFAULT_DATE_FMT,), locale=locale, earliest=earliest, latest=latest, fix_default=fix_default
|
|
550
554
|
)
|
|
551
555
|
|
|
552
556
|
|
|
553
557
|
class Time(DateTimeInput[time], type=time):
|
|
558
|
+
"""
|
|
559
|
+
Input type that accepts any number of datetime format strings for parsing input. Parsing results in returning
|
|
560
|
+
a :class:`python:datetime.time` object.
|
|
561
|
+
|
|
562
|
+
:param formats: One or more :ref:`datetime format strings <python:strftime-strptime-behavior>`. Defaults to
|
|
563
|
+
:data:`DEFAULT_DT_FMT`.
|
|
564
|
+
:param locale: An alternate locale to use when parsing input
|
|
565
|
+
:param earliest: If specified, the parsed value must be later than or equal to this
|
|
566
|
+
:param latest: If specified, the parsed value must be earlier than or equal to this
|
|
567
|
+
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
568
|
+
"""
|
|
569
|
+
|
|
554
570
|
def __init__(
|
|
555
571
|
self,
|
|
556
572
|
*formats: str,
|
|
@@ -559,17 +575,6 @@ class Time(DateTimeInput[time], type=time):
|
|
|
559
575
|
latest: TimeBound = None,
|
|
560
576
|
fix_default: Bool = True,
|
|
561
577
|
):
|
|
562
|
-
"""
|
|
563
|
-
Input type that accepts any number of datetime format strings for parsing input. Parsing results in returning
|
|
564
|
-
a :class:`python:datetime.time` object.
|
|
565
|
-
|
|
566
|
-
:param formats: One or more :ref:`datetime format strings <python:strftime-strptime-behavior>`. Defaults to
|
|
567
|
-
:data:`DEFAULT_DT_FMT`.
|
|
568
|
-
:param locale: An alternate locale to use when parsing input
|
|
569
|
-
:param earliest: If specified, the parsed value must be later than or equal to this
|
|
570
|
-
:param latest: If specified, the parsed value must be earlier than or equal to this
|
|
571
|
-
:param fix_default: Whether default values should be normalized using :meth:`~DTInput.fix_default`.
|
|
572
|
-
"""
|
|
573
578
|
super().__init__(
|
|
574
579
|
formats or (DEFAULT_TIME_FMT,), locale=locale, earliest=earliest, latest=latest, fix_default=fix_default
|
|
575
580
|
)
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/utils.py
RENAMED
|
@@ -11,7 +11,7 @@ import warnings
|
|
|
11
11
|
from contextlib import contextmanager
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from stat import S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLNK, S_IFMT, S_IFREG, S_IFSOCK
|
|
14
|
-
from typing import TYPE_CHECKING, Any, BinaryIO,
|
|
14
|
+
from typing import TYPE_CHECKING, Any, BinaryIO, Iterator, TextIO, Union
|
|
15
15
|
from weakref import finalize
|
|
16
16
|
|
|
17
17
|
from ..utils import FixedFlag
|
|
@@ -169,7 +169,7 @@ class FileWrapper:
|
|
|
169
169
|
self._close()
|
|
170
170
|
|
|
171
171
|
@contextmanager
|
|
172
|
-
def _file(self) ->
|
|
172
|
+
def _file(self) -> Iterator[FP]:
|
|
173
173
|
try:
|
|
174
174
|
yield self._open()
|
|
175
175
|
finally:
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
from .base import BaseOption, BasePositional, Parameter
|
|
2
|
+
from .choice_map import Action, SubCommand
|
|
3
|
+
from .groups import ParamGroup
|
|
4
|
+
from .options import ActionFlag, Counter, Flag, Option, TriFlag, action_flag, after_main, before_main, help_action
|
|
5
|
+
from .pass_thru import PassThru
|
|
6
|
+
from .positionals import Positional
|
|
@@ -410,6 +410,15 @@ class ActionFlag(Flag, repr_attrs=('order', 'before_main')):
|
|
|
410
410
|
# Note: If func is None, then CommandParameters._process_action_flags raises ParameterDefinitionError
|
|
411
411
|
return partial(self.func, command) # imitates a bound method
|
|
412
412
|
|
|
413
|
+
def __reduce__(self):
|
|
414
|
+
# When a string is returned, it is treated as the name of a global variable. Since this class acts as a
|
|
415
|
+
# decorator that replaces the decorated method, this approach is necessary to prevent the following kind of
|
|
416
|
+
# PicklingError: Can't pickle <function help_action ...>: it's not the same object as ...options.help_action
|
|
417
|
+
if self._func is not None:
|
|
418
|
+
return self.__qualname__
|
|
419
|
+
else: # This is not generally expected
|
|
420
|
+
return super().__reduce__()
|
|
421
|
+
|
|
413
422
|
|
|
414
423
|
#: Alias for :class:`ActionFlag`
|
|
415
424
|
action_flag = ActionFlag # pylint: disable=C0103
|
|
@@ -12,11 +12,11 @@ from ..exceptions import ParameterDefinitionError
|
|
|
12
12
|
from ..inputs import normalize_input_type
|
|
13
13
|
from ..nargs import Nargs, NargsValue
|
|
14
14
|
from ..utils import _NotSet
|
|
15
|
-
from .actions import
|
|
16
|
-
from .base import
|
|
15
|
+
from .actions import Append, Store
|
|
16
|
+
from .base import AllowLeadingDashProperty, BasePositional
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
19
|
-
from ..typing import
|
|
19
|
+
from ..typing import ChoicesType, DefaultFunc, InputTypeFunc, LeadingDash
|
|
20
20
|
|
|
21
21
|
__all__ = ['Positional']
|
|
22
22
|
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/testing.py
RENAMED
|
@@ -13,7 +13,7 @@ from difflib import unified_diff
|
|
|
13
13
|
from io import BytesIO, StringIO
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from tempfile import TemporaryDirectory
|
|
16
|
-
from typing import IO, TYPE_CHECKING, Any, Callable,
|
|
16
|
+
from typing import IO, TYPE_CHECKING, Any, Callable, Iterable, Iterator, Type, Union
|
|
17
17
|
from unittest import TestCase
|
|
18
18
|
from unittest.mock import Mock, patch, seal
|
|
19
19
|
|
|
@@ -39,16 +39,16 @@ __all__ = [
|
|
|
39
39
|
'TemporaryDir',
|
|
40
40
|
]
|
|
41
41
|
|
|
42
|
-
Argv =
|
|
43
|
-
Expected =
|
|
44
|
-
Kwargs =
|
|
45
|
-
Env =
|
|
46
|
-
Case =
|
|
47
|
-
EnvCase =
|
|
42
|
+
Argv = list[str]
|
|
43
|
+
Expected = dict[str, Any]
|
|
44
|
+
Kwargs = dict[str, Any]
|
|
45
|
+
Env = dict[str, str]
|
|
46
|
+
Case = tuple[Argv, Expected]
|
|
47
|
+
EnvCase = tuple[Argv, Env, Expected]
|
|
48
48
|
ExcType = Type[Exception]
|
|
49
|
-
ExceptionCase = Union[Argv,
|
|
49
|
+
ExceptionCase = Union[Argv, tuple[Argv, ExcType], tuple[Argv, ExcType, str]]
|
|
50
50
|
ExcCases = Iterable[ExceptionCase]
|
|
51
|
-
CallExceptionCase = Union[
|
|
51
|
+
CallExceptionCase = Union[tuple[Kwargs, ExcType], tuple[Kwargs, ExcType, str]]
|
|
52
52
|
CallExceptionCases = Iterable[CallExceptionCase]
|
|
53
53
|
|
|
54
54
|
OPT_ENV_MOD = 'cli_command_parser.parser.environ'
|
|
@@ -344,7 +344,7 @@ def sealed_mock(*args, **kwargs):
|
|
|
344
344
|
|
|
345
345
|
|
|
346
346
|
@contextmanager
|
|
347
|
-
def load_command(directory: Path, name: str, cmd_name: str, **kwargs) ->
|
|
347
|
+
def load_command(directory: Path, name: str, cmd_name: str, **kwargs) -> Iterator[CommandCls]:
|
|
348
348
|
path = directory.joinpath(name)
|
|
349
349
|
with Context.for_prog(path, **kwargs):
|
|
350
350
|
yield load_commands(path)[cmd_name]
|
|
@@ -16,7 +16,6 @@ lib/cli_command_parser/config.py
|
|
|
16
16
|
lib/cli_command_parser/context.py
|
|
17
17
|
lib/cli_command_parser/core.py
|
|
18
18
|
lib/cli_command_parser/documentation.py
|
|
19
|
-
lib/cli_command_parser/error_handling.py
|
|
20
19
|
lib/cli_command_parser/exceptions.py
|
|
21
20
|
lib/cli_command_parser/metadata.py
|
|
22
21
|
lib/cli_command_parser/nargs.py
|
|
@@ -39,6 +38,10 @@ lib/cli_command_parser/conversion/cli.py
|
|
|
39
38
|
lib/cli_command_parser/conversion/command_builder.py
|
|
40
39
|
lib/cli_command_parser/conversion/utils.py
|
|
41
40
|
lib/cli_command_parser/conversion/visitor.py
|
|
41
|
+
lib/cli_command_parser/error_handling/__init__.py
|
|
42
|
+
lib/cli_command_parser/error_handling/base.py
|
|
43
|
+
lib/cli_command_parser/error_handling/other.py
|
|
44
|
+
lib/cli_command_parser/error_handling/windows.py
|
|
42
45
|
lib/cli_command_parser/formatting/__init__.py
|
|
43
46
|
lib/cli_command_parser/formatting/commands.py
|
|
44
47
|
lib/cli_command_parser/formatting/params.py
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
from .base import Parameter, BasePositional, BaseOption
|
|
2
|
-
from .choice_map import SubCommand, Action
|
|
3
|
-
from .groups import ParamGroup
|
|
4
|
-
from .options import Option, Flag, Counter, ActionFlag, action_flag, before_main, after_main, TriFlag, help_action
|
|
5
|
-
from .pass_thru import PassThru
|
|
6
|
-
from .positionals import Positional
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/__main__.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/annotations.py
RENAMED
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/commands.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/compat.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/config.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/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
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/core.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/base.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/inputs/files.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/metadata.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/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
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parse_tree.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/parser.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/lib/cli_command_parser/typing.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.5.10 → cli_command_parser-2025.6.14}/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|