cli-command-parser 2025.11.1__tar.gz → 2026.2.1__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.11.1 → cli_command_parser-2026.2.1}/PKG-INFO +1 -1
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/__version__.py +1 -1
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/command_parameters.py +2 -10
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/core.py +1 -1
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/__init__.py +2 -2
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/base.py +9 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/numeric.py +126 -11
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/time.py +2 -7
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/base.py +7 -1
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser.egg-info/PKG-INFO +1 -1
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/LICENSE +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/MANIFEST.in +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/__init__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/__main__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/annotations.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/commands.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/compat.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/config.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/context.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/__init__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/__main__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/argparse_ast.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/argparse_utils.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/cli.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/command_builder.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/utils.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/conversion/visitor.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/documentation.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/error_handling/__init__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/error_handling/base.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/error_handling/other.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/error_handling/windows.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/exceptions.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/formatting/__init__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/formatting/commands.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/formatting/params.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/formatting/restructured_text.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/formatting/utils.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/choices.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/exceptions.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/files.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/patterns.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/utils.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/metadata.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/nargs.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/__init__.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/actions.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/choice_map.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/groups.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/option_strings.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/options.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/pass_thru.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parameters/positionals.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parse_tree.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parser.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/testing.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/typing.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/utils.py +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser.egg-info/SOURCES.txt +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser.egg-info/dependency_links.txt +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser.egg-info/entry_points.txt +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser.egg-info/requires.txt +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser.egg-info/top_level.txt +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/pyproject.toml +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/readme.rst +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/requirements-dev.txt +0 -0
- {cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/setup.cfg +0 -0
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/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__ = '
|
|
4
|
+
__version__ = '2026.02.01'
|
|
5
5
|
__author__ = 'Doug Skrypa'
|
|
6
6
|
__author_email__ = 'dskrypa@gmail.com'
|
|
7
7
|
__license__ = 'Apache 2.0'
|
|
@@ -34,7 +34,6 @@ class CommandParameters:
|
|
|
34
34
|
# fmt: off
|
|
35
35
|
command: CommandCls #: The Command associated with this CommandParameters object
|
|
36
36
|
formatter: CommandHelpFormatter #: The formatter used for this Command's help text
|
|
37
|
-
command_parent: CommandCls | None #: The parent Command, if any
|
|
38
37
|
parent: CommandParameters | None #: The parent Command's CommandParameters
|
|
39
38
|
action: Action | None = None #: An Action Parameter, if specified
|
|
40
39
|
_pass_thru: PassThru | None = None #: A PassThru Parameter, if specified
|
|
@@ -49,15 +48,8 @@ class CommandParameters:
|
|
|
49
48
|
option_map: OptionMap #: Mapping of {--opt / -opt: Parameter}
|
|
50
49
|
# fmt: on
|
|
51
50
|
|
|
52
|
-
def __init__(
|
|
53
|
-
self,
|
|
54
|
-
command: CommandCls,
|
|
55
|
-
command_parent: CommandCls | None,
|
|
56
|
-
parent_params: CommandParameters | None,
|
|
57
|
-
config: CommandConfig,
|
|
58
|
-
):
|
|
51
|
+
def __init__(self, command: CommandCls, parent_params: CommandParameters | None, config: CommandConfig):
|
|
59
52
|
self.command = command
|
|
60
|
-
self.command_parent = command_parent
|
|
61
53
|
self.parent = parent_params
|
|
62
54
|
self.config = config
|
|
63
55
|
self._process_parameters()
|
|
@@ -167,7 +159,7 @@ class CommandParameters:
|
|
|
167
159
|
while param_group := param_group.group:
|
|
168
160
|
groups.add(param_group)
|
|
169
161
|
|
|
170
|
-
if self.config.add_help and self.
|
|
162
|
+
if self.config.add_help and self.parent is not None and not self.parent._has_help:
|
|
171
163
|
options.append(help_action)
|
|
172
164
|
|
|
173
165
|
self._process_positionals(positionals)
|
|
@@ -211,7 +211,7 @@ class CommandMeta(ABCMeta, type):
|
|
|
211
211
|
cls = cls.__class__
|
|
212
212
|
parent = mcs.parent(cls, True)
|
|
213
213
|
parent_params = mcs.params(parent) if parent is not None else None
|
|
214
|
-
cls.__params = params = CommandParameters(cls,
|
|
214
|
+
cls.__params = params = CommandParameters(cls, parent_params, mcs.config(cls, DEFAULT_CONFIG))
|
|
215
215
|
return params
|
|
216
216
|
|
|
217
217
|
@classmethod
|
|
@@ -15,7 +15,7 @@ from .base import InputType
|
|
|
15
15
|
from .choices import ChoiceMap, Choices, EnumChoices
|
|
16
16
|
from .exceptions import InputValidationError, InvalidChoiceError
|
|
17
17
|
from .files import File, Json, Path, Pickle, Serialized
|
|
18
|
-
from .numeric import NumRange, Range
|
|
18
|
+
from .numeric import Bytes, NumRange, Range
|
|
19
19
|
from .patterns import Glob, Regex, RegexMode
|
|
20
20
|
from .time import Date, DateTime, Day, DTFormatMode, Month, Time, TimeDelta
|
|
21
21
|
from .utils import FileWrapper, StatMode
|
|
@@ -26,7 +26,7 @@ if _t.TYPE_CHECKING:
|
|
|
26
26
|
# fmt: off
|
|
27
27
|
__all__ = [
|
|
28
28
|
'StatMode', 'FileWrapper', 'Path', 'File', 'Serialized', 'Json', 'Pickle',
|
|
29
|
-
'Range', 'NumRange',
|
|
29
|
+
'Bytes', 'Range', 'NumRange',
|
|
30
30
|
'Choices', 'ChoiceMap', 'EnumChoices',
|
|
31
31
|
'Regex', 'RegexMode', 'Glob',
|
|
32
32
|
'Day', 'Month', 'TimeDelta', 'DateTime', 'Date', 'Time', 'DTFormatMode',
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/base.py
RENAMED
|
@@ -43,3 +43,12 @@ class InputType(Generic[T], ABC):
|
|
|
43
43
|
def format_metavar(self, choice_delim: str = ',', sort_choices: bool = False) -> str:
|
|
44
44
|
# TODO: Optional/required arg, or handle wrapping in []/{} in formatter
|
|
45
45
|
return NotImplemented
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _FixedInputType(InputType[T], ABC):
|
|
49
|
+
__slots__ = ()
|
|
50
|
+
|
|
51
|
+
def fix_default(self, value: str | T | None) -> T | None:
|
|
52
|
+
if value is None or not isinstance(value, str) or not self._fix_default:
|
|
53
|
+
return value
|
|
54
|
+
return self(value)
|
|
@@ -7,20 +7,25 @@ Custom numeric input handlers for Parameters
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import re
|
|
10
11
|
from abc import ABC, abstractmethod
|
|
12
|
+
from typing import Literal
|
|
11
13
|
|
|
12
14
|
from ..typing import NT, Bool, Number, NumType, RngType
|
|
13
|
-
from .base import
|
|
15
|
+
from .base import _FixedInputType
|
|
14
16
|
from .exceptions import InputValidationError
|
|
15
17
|
from .utils import RangeMixin, range_str
|
|
16
18
|
|
|
17
|
-
__all__ = ['Range', 'NumRange']
|
|
19
|
+
__all__ = ['NumericInput', 'Range', 'NumRange', 'Bytes']
|
|
18
20
|
|
|
19
21
|
_range = range
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class NumericInput(
|
|
24
|
+
class NumericInput(_FixedInputType[NT], ABC):
|
|
23
25
|
__slots__ = ()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _RangeInput(NumericInput[NT], ABC):
|
|
24
29
|
type: NumType
|
|
25
30
|
|
|
26
31
|
def is_valid_type(self, value: str) -> bool:
|
|
@@ -45,13 +50,8 @@ class NumericInput(InputType[NT], ABC):
|
|
|
45
50
|
def format_metavar(self, choice_delim: str = ',', sort_choices: bool = False) -> str:
|
|
46
51
|
return f'{{{self._range_str()}}}'
|
|
47
52
|
|
|
48
|
-
def fix_default(self, value: str | NT | None) -> NT | None:
|
|
49
|
-
if value is None or not isinstance(value, str) or not self._fix_default:
|
|
50
|
-
return value
|
|
51
|
-
return self(value)
|
|
52
|
-
|
|
53
53
|
|
|
54
|
-
class Range(
|
|
54
|
+
class Range(_RangeInput[NT]):
|
|
55
55
|
"""
|
|
56
56
|
A range of integers that uses the builtin :class:`python:range`. If a range object is passed to a
|
|
57
57
|
:class:`.Parameter` as the ``type=`` value, it will automatically be wrapped by this class.
|
|
@@ -75,7 +75,7 @@ class Range(NumericInput[NT]):
|
|
|
75
75
|
if isinstance(range, int):
|
|
76
76
|
self.range = _range(range)
|
|
77
77
|
elif not isinstance(range, _range):
|
|
78
|
-
self.range = _range(*range)
|
|
78
|
+
self.range = _range(*range) # noqa
|
|
79
79
|
else:
|
|
80
80
|
self.range = range
|
|
81
81
|
if type is not None:
|
|
@@ -102,7 +102,7 @@ class Range(NumericInput[NT]):
|
|
|
102
102
|
raise InputValidationError(f'expected a value in the range {self._range_str()}')
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
class NumRange(RangeMixin,
|
|
105
|
+
class NumRange(RangeMixin, _RangeInput[NT]):
|
|
106
106
|
"""
|
|
107
107
|
A range of integers or floats, optionally only bounded on one side.
|
|
108
108
|
|
|
@@ -191,3 +191,118 @@ class NumRange(RangeMixin, NumericInput[NT]):
|
|
|
191
191
|
return self.handle_invalid(self.max, self.include_max, -1)
|
|
192
192
|
else:
|
|
193
193
|
return value
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class Bytes(NumericInput[NT]):
|
|
197
|
+
"""
|
|
198
|
+
A byte count/size.
|
|
199
|
+
|
|
200
|
+
Types of user inputs that are accepted:
|
|
201
|
+
|
|
202
|
+
- Simple integer representing an exact byte count
|
|
203
|
+
- Number with a unit (KB, MiB, etc.), which will result in the processed value being the raw byte count after
|
|
204
|
+
scaling based on the provided unit
|
|
205
|
+
|
|
206
|
+
:base: Whether 2-character units (MB, GB, etc.) are treated as base 2 (historical interpretation) or
|
|
207
|
+
base 10 (the default) (following the International System of Units (SI) definition). Regardless of the base
|
|
208
|
+
specified here, user-provided units that explicitly use an ``i`` to indicate binary (MiB, GiB, etc.) will always
|
|
209
|
+
be treated as base 2.
|
|
210
|
+
:short: Whether single-letter units (other than ``B``, which is always valid) are accepted (default: True). When
|
|
211
|
+
accepted, they use the ``base`` parameter to determine which base to use.
|
|
212
|
+
:fractions: Whether fractional values are allowed (e.g., ``1.2 MB``). By default, only integers / whole numbers
|
|
213
|
+
are accepted.
|
|
214
|
+
:negative: Whether negative numeric values are accepted.
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
__slots__ = ('base', 'short', 'fractions', 'negative')
|
|
218
|
+
_pattern = re.compile(r'^(-?\d+(?:\.\d+)?)\s*([KMGTPEZYRQ]?i?B?)$', re.IGNORECASE)
|
|
219
|
+
_PREFIXES = 'KMGTPEZYRQ'
|
|
220
|
+
|
|
221
|
+
def __init__(
|
|
222
|
+
self,
|
|
223
|
+
base: Literal[2, 10] = 10,
|
|
224
|
+
*,
|
|
225
|
+
short: Bool = True,
|
|
226
|
+
fractions: Bool = False,
|
|
227
|
+
negative: Bool = False,
|
|
228
|
+
fix_default: Bool = True,
|
|
229
|
+
):
|
|
230
|
+
if base not in (2, 10):
|
|
231
|
+
raise ValueError(f'Invalid 2-character unit {base=} - expected 2 for binary or 10 for SI / decimal')
|
|
232
|
+
super().__init__(fix_default)
|
|
233
|
+
self.base = base
|
|
234
|
+
self.short = short
|
|
235
|
+
self.fractions = fractions
|
|
236
|
+
self.negative = negative
|
|
237
|
+
|
|
238
|
+
def __repr__(self) -> str:
|
|
239
|
+
base, short, fractions, negative = self.base, self.short, self.fractions, self.negative
|
|
240
|
+
return f'<{self.__class__.__name__}(({base=}, {short=}, {fractions=}, {negative=})>'
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
def is_valid_type(cls, value: str) -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Called during parsing when :meth:`.ParamAction.would_accept` is called to determine if the value would be
|
|
246
|
+
accepted later for processing / conversion when called.
|
|
247
|
+
|
|
248
|
+
:param value: The parsed argument to validate
|
|
249
|
+
:return: True if this input would accept it for processing later (where it may still be rejected), False if
|
|
250
|
+
it should be rejected before attempting to process / convert / store it.
|
|
251
|
+
"""
|
|
252
|
+
try:
|
|
253
|
+
return bool(cls._pattern.match(value))
|
|
254
|
+
except TypeError:
|
|
255
|
+
return False
|
|
256
|
+
|
|
257
|
+
def format_metavar(self, choice_delim: str = ',', sort_choices: bool = False) -> str:
|
|
258
|
+
return 'BYTES[B|KB|MiB|...]'
|
|
259
|
+
|
|
260
|
+
def _type_desc(self) -> str:
|
|
261
|
+
parts = ['a']
|
|
262
|
+
if not self.negative:
|
|
263
|
+
parts.append('positive')
|
|
264
|
+
|
|
265
|
+
if not self.fractions:
|
|
266
|
+
if len(parts) == 1:
|
|
267
|
+
parts[0] = 'an'
|
|
268
|
+
parts.append('integer')
|
|
269
|
+
|
|
270
|
+
parts.append('byte count/size')
|
|
271
|
+
return ' '.join(parts)
|
|
272
|
+
|
|
273
|
+
def __call__(self, value: str) -> NT:
|
|
274
|
+
try:
|
|
275
|
+
num, unit = self._pattern.match(value.strip()).groups()
|
|
276
|
+
except (TypeError, AttributeError):
|
|
277
|
+
raise InputValidationError(f'expected {self._type_desc()} with optional unit') from None
|
|
278
|
+
|
|
279
|
+
try:
|
|
280
|
+
num = float(num) if self.fractions else int(num)
|
|
281
|
+
except ValueError as e: # This should only be caused when int is expected at this point
|
|
282
|
+
raise InputValidationError(f'expected {self._type_desc()}, but found {num!r}') from e
|
|
283
|
+
|
|
284
|
+
if not self.negative and num < 0:
|
|
285
|
+
raise InputValidationError(f'expected {self._type_desc()}, but found {num!r}')
|
|
286
|
+
|
|
287
|
+
return num * self._get_multiplier(unit)
|
|
288
|
+
|
|
289
|
+
def _get_multiplier(self, unit: str | None) -> int:
|
|
290
|
+
if not unit:
|
|
291
|
+
return 1
|
|
292
|
+
|
|
293
|
+
uc_unit = unit.upper()
|
|
294
|
+
if uc_unit == 'B':
|
|
295
|
+
return 1
|
|
296
|
+
elif uc_unit.endswith('I') or (not self.short and len(uc_unit) == 1):
|
|
297
|
+
# `i` alone or Ki/Mi/etc, or only a single character was provided
|
|
298
|
+
raise InputValidationError(f'invalid byte {unit=}')
|
|
299
|
+
|
|
300
|
+
try:
|
|
301
|
+
exp = self._PREFIXES.index(uc_unit[0]) + 1
|
|
302
|
+
except ValueError:
|
|
303
|
+
raise InputValidationError(f'invalid byte {unit=}')
|
|
304
|
+
|
|
305
|
+
if 'I' in uc_unit or self.base == 2:
|
|
306
|
+
return 1024**exp
|
|
307
|
+
else:
|
|
308
|
+
return 1000**exp
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/time.py
RENAMED
|
@@ -26,7 +26,7 @@ from typing import Collection, Iterator, Literal, Sequence, Type, TypeVar, overl
|
|
|
26
26
|
|
|
27
27
|
from ..typing import Bool, Locale, Number, T, TimeBound
|
|
28
28
|
from ..utils import MissingMixin
|
|
29
|
-
from .base import InputType
|
|
29
|
+
from .base import InputType, _FixedInputType
|
|
30
30
|
from .exceptions import InputValidationError, InvalidChoiceError
|
|
31
31
|
from .utils import RangeMixin, range_str
|
|
32
32
|
|
|
@@ -73,7 +73,7 @@ class different_locale:
|
|
|
73
73
|
self._lock.release()
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
class DTInput(
|
|
76
|
+
class DTInput(_FixedInputType[T], ABC):
|
|
77
77
|
__slots__ = ('locale',)
|
|
78
78
|
dt_type: str
|
|
79
79
|
locale: Locale | None
|
|
@@ -93,11 +93,6 @@ class DTInput(InputType[T], ABC):
|
|
|
93
93
|
def choice_str(self, choice_delim: str = ',', sort_choices: bool = False) -> str:
|
|
94
94
|
raise NotImplementedError
|
|
95
95
|
|
|
96
|
-
def fix_default(self, value: str | T | None) -> T | None:
|
|
97
|
-
if value is None or not isinstance(value, str) or not self._fix_default:
|
|
98
|
-
return value
|
|
99
|
-
return self(value)
|
|
100
|
-
|
|
101
96
|
|
|
102
97
|
# region Calendar Unit Inputs
|
|
103
98
|
|
|
@@ -20,6 +20,7 @@ from ..exceptions import BadArgument, InvalidChoice, MissingArgument, ParameterD
|
|
|
20
20
|
from ..inputs import InputType, normalize_input_type
|
|
21
21
|
from ..inputs.choices import _ChoicesBase
|
|
22
22
|
from ..inputs.exceptions import InputValidationError, InvalidChoiceError
|
|
23
|
+
from ..inputs.numeric import NumericInput
|
|
23
24
|
from ..nargs import REMAINDER, Nargs
|
|
24
25
|
from ..typing import CommandMethod, DefaultFunc, T_co
|
|
25
26
|
from ..utils import _NotSet
|
|
@@ -360,7 +361,12 @@ class Parameter(ParamBase, Generic[T_co], ABC):
|
|
|
360
361
|
if not isinstance(value, str) or not value or not value[0] == '-':
|
|
361
362
|
return
|
|
362
363
|
elif self.allow_leading_dash == AllowLeadingDash.NUMERIC:
|
|
363
|
-
|
|
364
|
+
# `joined` indicates the value was provided as --opt=val, so it's explicitly intended for this param.
|
|
365
|
+
# If the value is obviously numeric, that has been allowed via config.
|
|
366
|
+
# If this param's type is a numeric input type, it is allowed because `prepare_validation_value` is always
|
|
367
|
+
# called before this method, and the type is validated there (some of this validation should definitely be
|
|
368
|
+
# simplified).
|
|
369
|
+
if not joined and len(value) > 1 and not _is_numeric(value) and not isinstance(self.type, NumericInput):
|
|
364
370
|
raise BadArgument(self, f'invalid {value=}')
|
|
365
371
|
elif self.allow_leading_dash == AllowLeadingDash.NEVER:
|
|
366
372
|
raise BadArgument(self, f'invalid {value=}')
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/__init__.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/__main__.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/annotations.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/commands.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/compat.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/config.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/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-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/documentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/files.py
RENAMED
|
File without changes
|
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/inputs/utils.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/metadata.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/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
|
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parse_tree.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/parser.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/testing.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/lib/cli_command_parser/typing.py
RENAMED
|
File without changes
|
{cli_command_parser-2025.11.1 → cli_command_parser-2026.2.1}/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
|
|
File without changes
|