1818flag (``dry_run`` -> ``--dry-run``); pass an explicit ``Option("--my_flag")`` to opt out.
1919Positional-only parameters (before ``/``) and ``**kwargs`` raise ``TypeError``. The parameter
2020names ``dest`` and ``subcommand`` are reserved; ``cmd2_statement`` receives the parsed
21- ``Statement`` and (with ``base_command=True``) ``cmd2_handler `` receives the subcommand handler:
21+ ``Statement`` and (with ``base_command=True``) ``cmd2_subcommand_func `` receives the subcommand handler:
2222
2323 class MyApp(cmd2.Cmd):
2424 @cmd2.with_annotated
@@ -118,7 +118,7 @@ def do_paint(
118118692 ``**parser_kwargs: Unpack[Cmd2ParserKwargs]``. Anything the parser ctor accepts -- ``description``,
119119``epilog``, ``prog``, ``usage``, ``parents``, ``argument_default``, ``prefix_chars``,
120120``fromfile_prefix_chars``, ``conflict_handler``, ``add_help``, ``allow_abbrev``, ``exit_on_error``,
121- ``formatter_class``, ``ap_completer_type ``, and on Python >= 3.14 ``suggest_on_error`` / ``color`` --
121+ ``formatter_class``, ``completer_class ``, and on Python >= 3.14 ``suggest_on_error`` / ``color`` --
122122flows straight through; the [`Cmd2ParserKwargs`][cmd2.annotated.Cmd2ParserKwargs] ``TypedDict`` is the single source of truth
123123and gives type-checkers/IDEs autocomplete on the decorator's call site. ``parser_class`` stays as
124124its own explicit kwarg because it selects the class itself, not a value passed to it. Two
@@ -181,8 +181,16 @@ def do_paint(
181181import functools
182182import inspect
183183import types
184- from collections .abc import Callable , Container , Iterable , Sequence
185- from dataclasses import dataclass , field
184+ from collections .abc import (
185+ Callable ,
186+ Container ,
187+ Iterable ,
188+ Sequence ,
189+ )
190+ from dataclasses import (
191+ dataclass ,
192+ field ,
193+ )
186194from pathlib import Path
187195from typing import (
188196 TYPE_CHECKING ,
@@ -203,14 +211,22 @@ def do_paint(
203211from rich .table import Column
204212
205213from . import constants
206- from .argparse_utils import DEFAULT_ARGUMENT_PARSER , Cmd2ArgumentParser , SubcommandSpec
214+ from .argparse_utils import (
215+ ArgparseCommandSpec ,
216+ Cmd2ArgumentParser ,
217+ SubcommandSpec ,
218+ )
207219from .completion import CompletionItem
208220from .decorators import _parse_positionals
209221from .exceptions import Cmd2ArgparseError
210222from .rich_utils import Cmd2HelpFormatter , HelpContent
211- from .types import CmdOrSetT , UnboundChoicesProvider , UnboundCompleter
223+ from .types import (
224+ CmdOrSetT ,
225+ UnboundChoicesProvider ,
226+ UnboundCompleter ,
227+ )
212228
213- if TYPE_CHECKING :
229+ if TYPE_CHECKING : # pragma: no cover
214230 from .argparse_completer import ArgparseCompleter
215231
216232#: ``nargs`` values accepted by cmd2's patched ``add_argument`` (incl. ranged tuples).
@@ -240,7 +256,7 @@ class Cmd2ParserKwargs(TypedDict, total=False):
240256 exit_on_error : bool
241257 suggest_on_error : bool
242258 color : bool
243- ap_completer_type : "type[ArgparseCompleter] | None"
259+ completer_class : "type[ArgparseCompleter] | None"
244260
245261
246262# ---------------------------------------------------------------------------
@@ -1687,7 +1703,7 @@ def _const_mismatches_type(a: _ArgparseArgument) -> bool:
16871703
16881704# Parameters handled specially by the decorator and not added to the parser. The first positional
16891705# parameter (self/cls) is always skipped by position; these cover additional decorator-managed names.
1690- _SKIP_PARAMS = frozenset ({"cmd2_handler" , "cmd2_statement" })
1706+ _SKIP_PARAMS = frozenset ({constants . NS_ATTR_SUBCOMMAND_FUNC , constants . NS_ATTR_STATEMENT })
16911707
16921708
16931709def _link_group_membership (
@@ -1718,14 +1734,17 @@ def _resolve_parameters(
17181734 """Resolve a function signature into a list of argparse-argument builders.
17191735
17201736 ``base_command`` marks each argument's context for the base-command :data:`_CONSTRAINTS` rows and
1721- drives the function-level ``cmd2_handler `` check below. ``groups``/``mutually_exclusive_groups``
1737+ drives the function-level ``cmd2_subcommand_func `` check below. ``groups``/``mutually_exclusive_groups``
17221738 are linked onto each argument as membership facts for the cross-config constraint rows.
17231739 """
17241740 sig = inspect .signature (func )
17251741 # Function-level check (not a per-argument _CONSTRAINTS row): a base command dispatches through
1726- # cmd2_handler, so it must exist. Here so it also fires when the function has zero parameters.
1727- if base_command and "cmd2_handler" not in sig .parameters :
1728- raise TypeError (f"with_annotated(base_command=True) requires a 'cmd2_handler' parameter in { func .__qualname__ } " )
1742+ # cmd2_subcommand_func, so it must exist. Here so it also fires when the function has zero parameters.
1743+ if base_command and constants .NS_ATTR_SUBCOMMAND_FUNC not in sig .parameters :
1744+ raise TypeError (
1745+ f"with_annotated(base_command=True) requires a '{ constants .NS_ATTR_SUBCOMMAND_FUNC } ' "
1746+ f"parameter in { func .__qualname__ } "
1747+ )
17291748 # Resolve hints only for the parameters that become arguments
17301749 ignored = {next (iter (sig .parameters ), None ), * skip_params }
17311750 ignored .discard (None )
@@ -1836,14 +1855,10 @@ def _filtered_namespace_kwargs(
18361855 exclude_subcommand : bool = False ,
18371856) -> dict [str , Any ]:
18381857 """Filter a parsed Namespace down to user-visible kwargs."""
1839- from .constants import NS_ATTR_SUBCMD_HANDLER
1840-
18411858 filtered : dict [str , Any ] = {}
18421859 for key , value in vars (ns ).items ():
18431860 if accepted is not None and key not in accepted :
18441861 continue
1845- if key == NS_ATTR_SUBCMD_HANDLER :
1846- continue
18471862 if exclude_subcommand and key == "subcommand" :
18481863 continue
18491864 filtered [key ] = value
@@ -1960,7 +1975,9 @@ def build_parser_from_function(
19601975 :param parser_kwargs: forwarded [`Cmd2ParserKwargs`][cmd2.annotated.Cmd2ParserKwargs]
19611976 :return: a fully configured ``Cmd2ArgumentParser``
19621977 """
1963- parser_cls = parser_class or DEFAULT_ARGUMENT_PARSER
1978+ from . import argparse_utils
1979+
1980+ parser_cls = parser_class or argparse_utils .DEFAULT_ARGUMENT_PARSER
19641981 if "description" not in parser_kwargs :
19651982 auto_description = _docstring_first_paragraph (func .__doc__ )
19661983 if auto_description is not None :
@@ -2113,10 +2130,10 @@ def _build_subcommand_handler(
21132130 def handler (self_arg : Any , ns : Any ) -> Any :
21142131 """Unpack Namespace into typed kwargs for the subcommand handler."""
21152132 filtered = _filtered_namespace_kwargs (ns , accepted = _accepted )
2116- if "cmd2_handler" in filtered :
2117- cmd2_h = filtered ["cmd2_handler" ]
2133+ if constants . NS_ATTR_SUBCOMMAND_FUNC in filtered :
2134+ cmd2_h = filtered [constants . NS_ATTR_SUBCOMMAND_FUNC ]
21182135 if isinstance (cmd2_h , functools .partial ) and cmd2_h .func is handler :
2119- filtered ["cmd2_handler" ] = None
2136+ filtered [constants . NS_ATTR_SUBCOMMAND_FUNC ] = None
21202137 return _invoke_command_func (
21212138 func , self_arg , filtered , leading_names = _leading_names , var_positional_name = _var_positional_name
21222139 )
@@ -2178,7 +2195,7 @@ def with_annotated(
21782195 :param ns_provider: callable returning a prepopulated Namespace (not with ``subcommand_to``)
21792196 :param preserve_quotes: preserve quotes in arguments (not with ``subcommand_to``)
21802197 :param with_unknown_args: capture unknown args as the ``_unknown`` kwarg (not with ``subcommand_to``)
2181- :param base_command: add ``add_subparsers()``; requires a ``cmd2_handler `` param and no positionals
2198+ :param base_command: add ``add_subparsers()``; requires a ``cmd2_subcommand_func `` param and no positionals
21822199 :param subcommand_to: parent command name; function must be named ``{parent_underscored}_{subcommand}``
21832200 :param help: subcommand help text (only with ``subcommand_to``)
21842201 :param aliases: alternative subcommand names (only with ``subcommand_to``)
@@ -2240,9 +2257,10 @@ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
22402257 if unknown_param .kind is inspect .Parameter .POSITIONAL_ONLY :
22412258 raise TypeError ("Parameter _unknown must be keyword-compatible when with_unknown_args=True" )
22422259
2243- if not base_command and "cmd2_handler" in inspect .signature (fn ).parameters :
2260+ if not base_command and constants . NS_ATTR_SUBCOMMAND_FUNC in inspect .signature (fn ).parameters :
22442261 raise TypeError (
2245- f"Parameter 'cmd2_handler' in { fn .__qualname__ } is only valid when with_annotated(base_command=True) is used."
2262+ f"Parameter '{ constants .NS_ATTR_SUBCOMMAND_FUNC } ' in { fn .__qualname__ } "
2263+ "is only valid when with_annotated(base_command=True) is used."
22462264 )
22472265
22482266 if subcommand_to is not None :
@@ -2252,15 +2270,15 @@ def decorator(fn: Callable[..., Any]) -> Callable[..., Any]:
22522270 base_command = base_command ,
22532271 options = options ,
22542272 )
2255- spec = SubcommandSpec (
2273+ subcommand_spec = SubcommandSpec (
22562274 name = subcmd_name ,
22572275 command = subcommand_to ,
22582276 help = help ,
22592277 aliases = tuple (aliases ),
22602278 deprecated = deprecated ,
22612279 parser_source = subcmd_parser_builder ,
22622280 )
2263- setattr (handler , constants .SUBCMD_ATTR_SPEC , spec )
2281+ setattr (handler , constants .SUBCOMMAND_ATTR_SPEC , subcommand_spec )
22642282 return handler
22652283
22662284 command_name = fn .__name__ [len (constants .COMMAND_FUNC_PREFIX ) :]
@@ -2304,10 +2322,10 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
23042322 raise Cmd2ArgparseError from exc
23052323
23062324 setattr (ns , constants .NS_ATTR_STATEMENT , statement )
2307- handler = getattr (ns , constants .NS_ATTR_SUBCMD_HANDLER , None )
2325+ handler = getattr (ns , constants .NS_ATTR_SUBCOMMAND_FUNC , None )
23082326 if base_command and handler is not None :
23092327 handler = functools .partial (handler , ns )
2310- ns . cmd2_handler = handler
2328+ setattr ( ns , constants . NS_ATTR_SUBCOMMAND_FUNC , handler )
23112329
23122330 func_kwargs = _filtered_namespace_kwargs (ns , accepted = accepted , exclude_subcommand = base_command )
23132331
@@ -2320,8 +2338,11 @@ def cmd_wrapper(*args: Any, **kwargs: Any) -> bool | None:
23202338 )
23212339 return result
23222340
2323- setattr (cmd_wrapper , constants .CMD_ATTR_PARSER_SOURCE , parser_builder )
2324- setattr (cmd_wrapper , constants .CMD_ATTR_PRESERVE_QUOTES , preserve_quotes )
2341+ argparse_command_spec = ArgparseCommandSpec (
2342+ parser_source = parser_builder ,
2343+ preserve_quotes = preserve_quotes ,
2344+ )
2345+ setattr (cmd_wrapper , constants .ARGPARSE_COMMAND_ATTR_SPEC , argparse_command_spec )
23252346
23262347 return cmd_wrapper
23272348
0 commit comments