cli-command-parser 2024.11.2__tar.gz → 2024.12.15__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.
Files changed (65) hide show
  1. {cli_command_parser-2024.11.2/lib/cli_command_parser.egg-info → cli_command_parser-2024.12.15}/PKG-INFO +1 -1
  2. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/__version__.py +1 -1
  3. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/exceptions.py +8 -8
  4. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/base.py +3 -3
  5. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/numeric.py +1 -1
  6. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/actions.py +21 -21
  7. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/base.py +16 -8
  8. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/options.py +9 -6
  9. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parser.py +4 -3
  10. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15/lib/cli_command_parser.egg-info}/PKG-INFO +1 -1
  11. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/LICENSE +0 -0
  12. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/MANIFEST.in +0 -0
  13. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/entry_points.txt +0 -0
  14. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/__init__.py +0 -0
  15. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/__main__.py +0 -0
  16. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/annotations.py +0 -0
  17. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/command_parameters.py +0 -0
  18. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/commands.py +0 -0
  19. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/compat.py +0 -0
  20. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/config.py +0 -0
  21. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/context.py +0 -0
  22. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/__init__.py +0 -0
  23. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/__main__.py +0 -0
  24. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/argparse_ast.py +0 -0
  25. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/argparse_utils.py +0 -0
  26. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/cli.py +0 -0
  27. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/command_builder.py +0 -0
  28. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/utils.py +0 -0
  29. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/conversion/visitor.py +0 -0
  30. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/core.py +0 -0
  31. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/documentation.py +0 -0
  32. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/error_handling.py +0 -0
  33. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/formatting/__init__.py +0 -0
  34. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/formatting/commands.py +0 -0
  35. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/formatting/params.py +0 -0
  36. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/formatting/restructured_text.py +0 -0
  37. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/formatting/utils.py +0 -0
  38. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/__init__.py +0 -0
  39. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/choices.py +0 -0
  40. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/exceptions.py +0 -0
  41. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/files.py +0 -0
  42. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/patterns.py +0 -0
  43. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/time.py +0 -0
  44. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/inputs/utils.py +0 -0
  45. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/metadata.py +0 -0
  46. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/nargs.py +0 -0
  47. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/__init__.py +0 -0
  48. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/choice_map.py +0 -0
  49. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/groups.py +0 -0
  50. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/option_strings.py +0 -0
  51. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/pass_thru.py +0 -0
  52. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parameters/positionals.py +0 -0
  53. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/parse_tree.py +0 -0
  54. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/testing.py +0 -0
  55. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/typing.py +0 -0
  56. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser/utils.py +0 -0
  57. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser.egg-info/SOURCES.txt +0 -0
  58. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser.egg-info/dependency_links.txt +0 -0
  59. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser.egg-info/entry_points.txt +0 -0
  60. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser.egg-info/requires.txt +0 -0
  61. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/lib/cli_command_parser.egg-info/top_level.txt +0 -0
  62. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/pyproject.toml +0 -0
  63. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/readme.rst +0 -0
  64. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/requirements-dev.txt +0 -0
  65. {cli_command_parser-2024.11.2 → cli_command_parser-2024.12.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cli_command_parser
3
- Version: 2024.11.2
3
+ Version: 2024.12.15
4
4
  Summary: CLI Command Parser
5
5
  Home-page: https://github.com/dskrypa/cli_command_parser
6
6
  Author: Doug Skrypa
@@ -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.11.02'
4
+ __version__ = '2024.12.15'
5
5
  __author__ = 'Doug Skrypa'
6
6
  __author_email__ = 'dskrypa@gmail.com'
7
7
  __license__ = 'Apache 2.0'
@@ -8,14 +8,14 @@ Exceptions for Command Parser
8
8
  from __future__ import annotations
9
9
 
10
10
  import sys
11
- from typing import TYPE_CHECKING, Any, Optional, Collection, Mapping
11
+ from typing import TYPE_CHECKING, Any, Collection, Mapping, Optional
12
12
 
13
13
  from .utils import _parse_tree_target_repr
14
14
 
15
15
  if TYPE_CHECKING:
16
- from .parameters import Parameter, BaseOption
16
+ from .parameters import BaseOption, Parameter
17
+ from .parse_tree import PosNode, Target, Word
17
18
  from .typing import ParamOrGroup
18
- from .parse_tree import PosNode, Word, Target
19
19
 
20
20
  __all__ = [
21
21
  'CommandParserException',
@@ -211,13 +211,13 @@ class BadArgument(ParamUsageError):
211
211
  class InvalidChoice(BadArgument):
212
212
  """Error raised when a value that does not match one of the pre-defined choices was provided for a Parameter"""
213
213
 
214
- def __init__(self, param: Optional[Parameter], invalid: Any, choices: Collection[Any]):
214
+ def __init__(self, param: Optional[Parameter], invalid: Any, choices: Collection[Any], env_var: str = None):
215
+ src = f' from env var={env_var!r}' if env_var else ''
215
216
  if isinstance(invalid, Collection) and not isinstance(invalid, str):
216
- bad_str = f'choices: {", ".join(map(repr, invalid))}'
217
+ bad_str = f'choices{src}: {", ".join(map(repr, invalid))}'
217
218
  else:
218
- bad_str = f'choice: {invalid!r}'
219
- choices_str = ', '.join(map(repr, choices))
220
- super().__init__(param, f'invalid {bad_str} (choose from: {choices_str})')
219
+ bad_str = f'choice{src}: {invalid!r}'
220
+ super().__init__(param, f'invalid {bad_str} (choose from: {", ".join(map(repr, choices))})')
221
221
 
222
222
 
223
223
  class MissingArgument(BadArgument):
@@ -7,7 +7,7 @@ Custom input handlers for Parameters
7
7
  from abc import ABC, abstractmethod
8
8
  from typing import Any, Generic, Optional
9
9
 
10
- from ..typing import T, Bool
10
+ from ..typing import Bool, T
11
11
 
12
12
  __all__ = ['InputType']
13
13
 
@@ -25,11 +25,11 @@ class InputType(Generic[T], ABC):
25
25
 
26
26
  def is_valid_type(self, value: str) -> bool: # pylint: disable=W0613
27
27
  """
28
- Called during parsing when :meth:`.Parameter.would_accept` is called to determine if the value would be
28
+ Called during parsing when :meth:`.ParamAction.would_accept` is called to determine if the value would be
29
29
  accepted later for processing / conversion via :meth:`.__call__`. May be overridden in subclasses to
30
30
  provide actual validation, if necessary.
31
31
 
32
- Not called by :meth:`.Parameter.take_action` - value validation should happen in :meth:`.__call__`
32
+ Note: value validation should happen in :meth:`.__call__`, not in this method.
33
33
 
34
34
  :param value: A parsed argument
35
35
  :return: True if this input would accept it for processing later (where it may still be rejected), False if
@@ -26,7 +26,7 @@ class NumericInput(InputType[NT], ABC):
26
26
 
27
27
  def is_valid_type(self, value: str) -> bool:
28
28
  """
29
- Called during parsing when :meth:`.Parameter.would_accept` is called to determine if the value would be
29
+ Called during parsing when :meth:`.ParamAction.would_accept` is called to determine if the value would be
30
30
  accepted later for processing / conversion when called.
31
31
 
32
32
  :param value: The parsed argument to validate
@@ -72,41 +72,41 @@ class ParamAction(ABC):
72
72
  # region Add Parsed Value / Constant Methods
73
73
 
74
74
  @abstractmethod
75
- def add_value(self, value: str, *, opt: str = None, combo: bool = False, joined: bool = False) -> Found:
75
+ def add_value(self, value: str, *, combo: bool = False, joined: bool = False, env_var: str = None) -> Found:
76
76
  """
77
77
  Execute this action for the given Parameter and value.
78
78
 
79
79
  :param value: The value that was provided, if any.
80
- :param opt: The option string that preceded the given value in the case of optional params, or that
81
- represents a flag so a constant value can be stored, if any.
82
80
  :param combo: Only True when a short option was provided, where the option string was combined with
83
81
  either a real value or a sequence of 1-char combinable versions of short option strings.
84
82
  :param joined: True if the value was provided as ``--option=value``, False otherwise.
83
+ :param env_var: The name of the environment variable that was used as the source of the given value, if
84
+ applicable.
85
85
  :return: The number of new values discovered
86
86
  """
87
87
  raise NotImplementedError
88
88
 
89
+ def add_env_value(self, value: str, env_var: str) -> Found:
90
+ return self.add_value(value, env_var=env_var)
91
+
89
92
  # Note: Not used yet
90
- # def add_values(self, values: Sequence[str], *, opt: str = None, combo: bool = False) -> Found:
93
+ # def add_values(self, values: Sequence[str], *, combo: bool = False) -> Found:
91
94
  # added = 0
92
95
  # for value in values:
93
- # added += self.add_value(value, opt=opt, combo=combo)
96
+ # added += self.add_value(value, combo=combo)
94
97
  # return added
95
98
 
96
99
  def add_const(self, *, opt: str = None, combo: bool = False) -> Found: # noqa
97
100
  ctx.record_action(self.param)
98
101
  raise MissingArgument(self.param)
99
102
 
100
- def add_env_value(self, value: str, env_var: str):
101
- return self.add_value(value)
102
-
103
103
  # endregion
104
104
 
105
105
  # region Parsing
106
106
 
107
107
  def would_accept(self, value: str, combo: bool = False) -> bool:
108
108
  try:
109
- normalized = self.param.prepare_value(value, combo, True)
109
+ normalized = self.param.prepare_validation_value(value, combo)
110
110
  except BadArgument:
111
111
  return False
112
112
  return self.param.is_valid_arg(normalized)
@@ -225,7 +225,7 @@ class ConstMixin:
225
225
  #
226
226
  # parsed.extend(consts)
227
227
 
228
- def add_env_value(self, value: str, env_var: str):
228
+ def add_env_value(self, value: str, env_var: str) -> Found:
229
229
  const, use_value = self.param.get_env_const(value, env_var)
230
230
  # The const may only be _NotSet once StoreValueOrConst / AppendValueOrConst are put into use
231
231
  # if const is _NotSet: # It does not support storing constants
@@ -250,15 +250,15 @@ class Store(ValueMixin, ParamAction, default=None, accepts_values=True):
250
250
 
251
251
  # region Add Parsed Value / Constant Methods
252
252
 
253
- def add_value(self, value: str, *, opt: str = None, combo: bool = False, joined: Bool = False) -> Found:
253
+ def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found:
254
254
  ctx.record_action(self.param)
255
- value = self.param.prepare_value(value, combo)
255
+ value = self.param.prepare_value(value, combo, env_var)
256
256
  self.param.validate(value, joined)
257
257
  self.set_value(value)
258
258
  return 1
259
259
 
260
260
  # Note: Not used yet
261
- # def add_values(self, values: Sequence[str], *, opt: str = None, combo: bool = False) -> Found:
261
+ # def add_values(self, values: Sequence[str], *, combo: bool = False) -> Found:
262
262
  # ctx.record_action(self.param)
263
263
  # if not values:
264
264
  # raise MissingArgument(self.param)
@@ -286,15 +286,15 @@ class Append(ValueMixin, ParamAction, accepts_values=True):
286
286
 
287
287
  # region Add Parsed Value / Constant Methods
288
288
 
289
- def add_value(self, value: str, *, opt: str = None, combo: bool = False, joined: Bool = False) -> Found:
289
+ def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found:
290
290
  ctx.record_action(self.param)
291
- value = self.param.prepare_value(value, combo)
291
+ value = self.param.prepare_value(value, combo, env_var)
292
292
  self.param.validate(value)
293
293
  self.append_value(value)
294
294
  return 1
295
295
 
296
296
  # Note: Not used yet
297
- # def add_values(self, values: Sequence[str], *, opt: str = None, combo: bool = False) -> Found:
297
+ # def add_values(self, values: Sequence[str], *, combo: bool = False) -> Found:
298
298
  # ctx.record_action(self.param)
299
299
  # if not values:
300
300
  # raise MissingArgument(self.param)
@@ -385,7 +385,7 @@ class BasicConstAction(ConstMixin, ParamAction, ABC, accepts_consts=True):
385
385
 
386
386
  # region Add Parsed Value / Constant Methods
387
387
 
388
- def add_value(self, value: str, *, opt: str = None, combo: bool = False, joined: Bool = False) -> Found: # noqa
388
+ def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found: # noqa
389
389
  ctx.record_action(self.param)
390
390
  raise BadArgument(self.param, f'does not accept values, but {value=} was provided')
391
391
 
@@ -471,9 +471,9 @@ class Count(ParamAction, accepts_values=True, accepts_consts=True):
471
471
  self._add(self.param.get_const(opt))
472
472
  return 1
473
473
 
474
- def add_value(self, value: str, *, opt: str = None, combo: bool = False, joined: Bool = False) -> Found:
474
+ def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found:
475
475
  ctx.record_action(self.param)
476
- value = self.param.prepare_value(value, combo)
476
+ value = self.param.prepare_value(value, combo, env_var)
477
477
  self.param.validate(value, joined)
478
478
  self._add(value)
479
479
  return 1
@@ -486,7 +486,7 @@ class Concatenate(Append):
486
486
 
487
487
  # region Add Parsed Value / Constant Methods
488
488
 
489
- def add_value(self, value: str, *, opt: str = None, combo: bool = False, joined: Bool = False) -> Found:
489
+ def add_value(self, value: str, *, combo: bool = False, joined: Bool = False, env_var: str = None) -> Found:
490
490
  param = self.param
491
491
  values = value.split()
492
492
  if not param.is_valid_arg(' '.join(values)):
@@ -525,7 +525,7 @@ class StoreAll(Store):
525
525
 
526
526
  # region Add Parsed Value / Constant Methods
527
527
 
528
- def add_values(self, values: list[str], *, opt: str = None, combo: bool = False) -> Found:
528
+ def add_values(self, values: list[str], *, combo: bool = False) -> Found:
529
529
  param = self.param
530
530
  ctx.record_action(param)
531
531
 
@@ -325,20 +325,28 @@ class Parameter(ParamBase, Generic[T_co], ABC):
325
325
  def get_env_const(self, value: str, env_var: str) -> tuple[T_co, bool]:
326
326
  return _NotSet, False
327
327
 
328
- def prepare_value(self, value: str, short_combo: Bool = False, pre_action: Bool = False) -> T_co:
329
- type_func = self.type
330
- if type_func is None or (pre_action and isinstance(type_func, InputType) and type_func.is_valid_type(value)):
328
+ def prepare_value(self, value: str, short_combo: Bool = False, env_var: str = None) -> T_co:
329
+ if self.type is None:
331
330
  return value
332
331
  try:
333
- return type_func(value)
332
+ return self.type(value)
334
333
  except InvalidChoiceError as e:
335
- raise InvalidChoice(self, e.invalid, e.choices) from e
334
+ raise InvalidChoice(self, e.invalid, e.choices, env_var) from e
336
335
  except InputValidationError as e:
337
- raise BadArgument(self, str(e)) from e
336
+ suffix = f' from env var={env_var!r}' if env_var else ''
337
+ raise BadArgument(self, f'invalid input{suffix} - {e}') from e
338
338
  except (TypeError, ValueError) as e:
339
- raise BadArgument(self, f'bad {value=} for type={type_func!r}: {e}') from e
339
+ suffix = f' from env var={env_var!r}' if env_var else ''
340
+ raise BadArgument(self, f'bad {value=} for type={self.type!r}{suffix}: {e}') from e
340
341
  except Exception as e:
341
- raise BadArgument(self, f'unable to cast {value=} to type={type_func!r}') from e
342
+ suffix = f' from env var={env_var!r}' if env_var else ''
343
+ raise BadArgument(self, f'unable to cast {value=} to type={self.type!r}{suffix}') from e
344
+
345
+ def prepare_validation_value(self, value: str, short_combo: Bool = False) -> T_co:
346
+ if self.type is None or (isinstance(self.type, InputType) and self.type.is_valid_type(value)):
347
+ return value
348
+ else:
349
+ return self.prepare_value(value, short_combo)
342
350
 
343
351
  def validate(self, value: Union[T_co, None], joined: Bool = False):
344
352
  if not isinstance(value, str) or not value or not value[0] == '-':
@@ -192,9 +192,9 @@ class Flag(BaseOption[Union[TD, TC]], actions=(StoreConst, AppendConst)):
192
192
  try:
193
193
  parsed = self.type(value)
194
194
  except Exception as e:
195
- raise ParamUsageError(self, f'unable to parse {value=} from {env_var=}: {e}') from e
195
+ raise ParamUsageError(self, f'unable to parse {value=} from env var={env_var!r}: {e}') from e
196
196
  if self.use_env_value and parsed != self.const and parsed != self.default:
197
- raise BadArgument(self, f'invalid value={parsed!r} from {env_var=}')
197
+ raise BadArgument(self, f'invalid value={parsed!r} from env var={env_var!r}')
198
198
  return parsed, self.use_env_value
199
199
 
200
200
 
@@ -308,10 +308,10 @@ class TriFlag(BaseOption[Union[TD, TC, TA]], ABC, actions=(StoreConst, AppendCon
308
308
  try:
309
309
  parsed = self.type(value)
310
310
  except Exception as e:
311
- raise ParamUsageError(self, f'unable to parse {value=} from {env_var=}: {e}') from e
311
+ raise ParamUsageError(self, f'unable to parse {value=} from env var={env_var!r}: {e}') from e
312
312
  if self.use_env_value:
313
313
  if parsed not in self.consts and parsed != self.default:
314
- raise BadArgument(self, f'invalid value={parsed!r} from {env_var=}')
314
+ raise BadArgument(self, f'invalid value={parsed!r} from env var={env_var!r}')
315
315
  return parsed, True
316
316
  else:
317
317
  const = self.consts[0] if parsed else self.consts[1]
@@ -491,14 +491,17 @@ class Counter(BaseOption[int], actions=(Count,)):
491
491
  self.default_cb = None
492
492
  return super().register_default_cb(method)
493
493
 
494
- def prepare_value(self, value: Optional[str], short_combo: bool = False, pre_action: bool = False) -> int:
494
+ def prepare_value(self, value: Optional[str], short_combo: bool = False, env_var: str = None) -> int:
495
495
  try:
496
496
  return self.type(value)
497
497
  except (ValueError, TypeError) as e:
498
498
  combinable = self.option_strs.combinable
499
499
  if short_combo and combinable and all(c in combinable for c in value):
500
500
  return len(value) + 1 # +1 for the -short that preceded this value
501
- raise BadArgument(self, f'bad counter {value=}') from e
501
+ suffix = f' from env var={env_var!r}' if env_var else ''
502
+ raise BadArgument(self, f'bad counter {value=}{suffix}') from e
503
+
504
+ prepare_validation_value = prepare_value
502
505
 
503
506
  def validate(self, value: Any, joined: Bool = False):
504
507
  if value is None or isinstance(value, self.type):
@@ -36,6 +36,9 @@ log = logging.getLogger(__name__)
36
36
 
37
37
  _PRE_INIT = ActionPhase.PRE_INIT
38
38
 
39
+ # TODO: When an invalid choice for a positional is provided with -h / --help, the invalid choice error is shown instead
40
+ # of help, but help should be shown instead
41
+
39
42
 
40
43
  class CommandParser:
41
44
  """Stateful parser used for a single pass of argument parsing"""
@@ -111,8 +114,6 @@ class CommandParser:
111
114
  self._parse_env_vars(ctx)
112
115
 
113
116
  def _parse_env_vars(self, ctx: Context):
114
- # TODO: It would be helpful to store arg provenance for error messages, especially for a conflict between
115
- # mutually exclusive params when they were provided via env
116
117
  for param in ctx.missing_options_with_env_var():
117
118
  for env_var in param.env_vars():
118
119
  try:
@@ -220,7 +221,7 @@ class CommandParser:
220
221
  self, opt: str, param: BaseOption, value: OptStr, combo: bool = False, joined: Bool = False
221
222
  ):
222
223
  if value is not None:
223
- param.action.add_value(value, opt=opt, combo=combo, joined=joined)
224
+ param.action.add_value(value, combo=combo, joined=joined)
224
225
  elif param.action.accepts_consts and not param.action.accepts_values:
225
226
  param.action.add_const(opt=opt, combo=combo)
226
227
  elif not self.consume_values(param) and param.action.accepts_consts:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cli_command_parser
3
- Version: 2024.11.2
3
+ Version: 2024.12.15
4
4
  Summary: CLI Command Parser
5
5
  Home-page: https://github.com/dskrypa/cli_command_parser
6
6
  Author: Doug Skrypa