Skip to content

Commit 5284772

Browse files
authored
Fixed custom types and moved common ones into types.py. (#1587)
1 parent e26703d commit 5284772

File tree

9 files changed

+146
-138
lines changed

9 files changed

+146
-138
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ cmd2/py_bridge.py @kmvanbrunt
4444
cmd2/rich_utils.py @kmvanbrunt
4545
cmd2/string_utils.py @kmvanbrunt
4646
cmd2/styles.py @tleonhardt @kmvanbrunt
47+
cmd2/types.py @tleonhardt @kmvanbrunt
4748
cmd2/utils.py @tleonhardt @kmvanbrunt
4849

4950
# Documentation

cmd2/argparse_custom.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -294,13 +294,14 @@ def get_choices(self) -> Choices:
294294

295295
from . import constants
296296
from . import rich_utils as ru
297-
from .completion import (
297+
from .completion import CompletionItem
298+
from .rich_utils import Cmd2RichArgparseConsole
299+
from .styles import Cmd2Style
300+
from .types import (
298301
ChoicesProviderUnbound,
302+
CmdOrSet,
299303
CompleterUnbound,
300-
CompletionItem,
301304
)
302-
from .rich_utils import Cmd2RichArgparseConsole
303-
from .styles import Cmd2Style
304305

305306
if TYPE_CHECKING: # pragma: no cover
306307
from .argparse_completer import ArgparseCompleter
@@ -384,7 +385,7 @@ class ChoicesCallable:
384385
def __init__(
385386
self,
386387
is_completer: bool,
387-
to_call: ChoicesProviderUnbound | CompleterUnbound,
388+
to_call: ChoicesProviderUnbound[CmdOrSet] | CompleterUnbound[CmdOrSet],
388389
) -> None:
389390
"""Initialize the ChoiceCallable instance.
390391
@@ -396,18 +397,18 @@ def __init__(
396397
self.to_call = to_call
397398

398399
@property
399-
def choices_provider(self) -> ChoicesProviderUnbound:
400+
def choices_provider(self) -> ChoicesProviderUnbound[CmdOrSet]:
400401
"""Retreive the internal choices_provider function."""
401402
if self.is_completer:
402403
raise AttributeError("This instance is configured as a completer, not a choices_provider")
403-
return cast(ChoicesProviderUnbound, self.to_call)
404+
return cast(ChoicesProviderUnbound[CmdOrSet], self.to_call)
404405

405406
@property
406-
def completer(self) -> CompleterUnbound:
407+
def completer(self) -> CompleterUnbound[CmdOrSet]:
407408
"""Retreive the internal completer function."""
408409
if not self.is_completer:
409410
raise AttributeError("This instance is configured as a choices_provider, not a completer")
410-
return cast(CompleterUnbound, self.to_call)
411+
return cast(CompleterUnbound[CmdOrSet], self.to_call)
411412

412413

413414
############################################################################################################
@@ -476,7 +477,7 @@ def _action_set_choices_callable(self: argparse.Action, choices_callable: Choice
476477

477478
def _action_set_choices_provider(
478479
self: argparse.Action,
479-
choices_provider: ChoicesProviderUnbound,
480+
choices_provider: ChoicesProviderUnbound[CmdOrSet],
480481
) -> None:
481482
"""Set choices_provider of an argparse Action.
482483
@@ -496,7 +497,7 @@ def _action_set_choices_provider(
496497

497498
def _action_set_completer(
498499
self: argparse.Action,
499-
completer: CompleterUnbound,
500+
completer: CompleterUnbound[CmdOrSet],
500501
) -> None:
501502
"""Set completer of an argparse Action.
502503
@@ -694,8 +695,8 @@ def _add_argument_wrapper(
694695
self: argparse._ActionsContainer,
695696
*args: Any,
696697
nargs: int | str | tuple[int] | tuple[int, int] | tuple[int, float] | None = None,
697-
choices_provider: ChoicesProviderUnbound | None = None,
698-
completer: CompleterUnbound | None = None,
698+
choices_provider: ChoicesProviderUnbound[CmdOrSet] | None = None,
699+
completer: CompleterUnbound[CmdOrSet] | None = None,
699700
suppress_tab_hint: bool = False,
700701
table_header: Sequence[str | Column] | None = None,
701702
**kwargs: Any,

cmd2/cmd2.py

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
TYPE_CHECKING,
6363
Any,
6464
TextIO,
65+
TypeAlias,
6566
TypeVar,
6667
Union,
6768
cast,
@@ -107,12 +108,8 @@
107108
)
108109
from .completion import (
109110
Choices,
110-
ChoicesProviderUnbound,
111-
CompleterBound,
112-
CompleterUnbound,
113111
CompletionItem,
114112
Completions,
115-
Matchable,
116113
)
117114
from .constants import (
118115
CLASS_ATTR_DEFAULT_HELP_CATEGORY,
@@ -121,7 +118,6 @@
121118
HELP_FUNC_PREFIX,
122119
)
123120
from .decorators import (
124-
CommandParent,
125121
as_subcommand_to,
126122
with_argparser,
127123
)
@@ -152,6 +148,12 @@
152148
RichPrintKwargs,
153149
)
154150
from .styles import Cmd2Style
151+
from .types import (
152+
ChoicesProviderUnbound,
153+
CmdOrSet,
154+
CompleterBound,
155+
CompleterUnbound,
156+
)
155157

156158
with contextlib.suppress(ImportError):
157159
from IPython import start_ipython
@@ -196,6 +198,13 @@ def __init__(self, msg: str = '') -> None:
196198
suggest_similar,
197199
)
198200

201+
if TYPE_CHECKING: # pragma: no cover
202+
StaticArgParseBuilder = staticmethod[[], argparse.ArgumentParser]
203+
ClassArgParseBuilder = classmethod['Cmd' | CommandSet, [], argparse.ArgumentParser]
204+
else:
205+
StaticArgParseBuilder = staticmethod
206+
ClassArgParseBuilder = classmethod
207+
199208

200209
class _SavedCmd2Env:
201210
"""cmd2 environment settings that are backed up when entering an interactive Python shell."""
@@ -209,14 +218,6 @@ def __init__(self) -> None:
209218
DisabledCommand = namedtuple('DisabledCommand', ['command_function', 'help_function', 'completer_function']) # noqa: PYI024
210219

211220

212-
if TYPE_CHECKING: # pragma: no cover
213-
StaticArgParseBuilder = staticmethod[[], argparse.ArgumentParser]
214-
ClassArgParseBuilder = classmethod['Cmd' | CommandSet, [], argparse.ArgumentParser]
215-
else:
216-
StaticArgParseBuilder = staticmethod
217-
ClassArgParseBuilder = classmethod
218-
219-
220221
class _CommandParsers:
221222
"""Create and store all command method argument parsers for a given Cmd instance.
222223
@@ -840,7 +841,7 @@ def register_command_set(self, cmdset: CommandSet) -> None:
840841

841842
def _build_parser(
842843
self,
843-
parent: CommandParent,
844+
parent: CmdOrSet,
844845
parser_builder: argparse.ArgumentParser
845846
| Callable[[], argparse.ArgumentParser]
846847
| StaticArgParseBuilder
@@ -849,7 +850,7 @@ def _build_parser(
849850
) -> argparse.ArgumentParser:
850851
"""Build argument parser for a command/subcommand.
851852
852-
:param parent: CommandParent object which owns the command using the parser.
853+
:param parent: object which owns the command using the parser.
853854
When parser_builder is a classmethod, this function passes
854855
parent's class to it.
855856
:param parser_builder: means used to build the parser
@@ -1821,7 +1822,7 @@ def basic_complete(
18211822
line: str, # noqa: ARG002
18221823
begidx: int, # noqa: ARG002
18231824
endidx: int, # noqa: ARG002
1824-
match_against: Iterable[Matchable],
1825+
match_against: Iterable[str | CompletionItem],
18251826
*,
18261827
sort: bool = True,
18271828
) -> Completions:
@@ -2193,8 +2194,8 @@ def _determine_ap_completer_type(parser: argparse.ArgumentParser) -> type[argpar
21932194
:param parser: the parser to examine
21942195
:return: type of ArgparseCompleter
21952196
"""
2196-
Completer = type[argparse_completer.ArgparseCompleter] | None # noqa: N806
2197-
completer_type: Completer = parser.get_ap_completer_type() # type: ignore[attr-defined]
2197+
APCompleterType: TypeAlias = type[argparse_completer.ArgparseCompleter] | None
2198+
completer_type: APCompleterType = parser.get_ap_completer_type() # type: ignore[attr-defined]
21982199

21992200
if completer_type is None:
22002201
completer_type = argparse_completer.DEFAULT_AP_COMPLETER
@@ -3283,8 +3284,8 @@ def _resolve_completer(
32833284
self,
32843285
preserve_quotes: bool = False,
32853286
choices: Iterable[Any] | None = None,
3286-
choices_provider: ChoicesProviderUnbound | None = None,
3287-
completer: CompleterUnbound | None = None,
3287+
choices_provider: ChoicesProviderUnbound[CmdOrSet] | None = None,
3288+
completer: CompleterUnbound[CmdOrSet] | None = None,
32883289
parser: argparse.ArgumentParser | None = None,
32893290
) -> Completer:
32903291
"""Determine the appropriate completer based on provided arguments."""
@@ -3315,8 +3316,8 @@ def read_input(
33153316
history: Sequence[str] | None = None,
33163317
preserve_quotes: bool = False,
33173318
choices: Iterable[Any] | None = None,
3318-
choices_provider: ChoicesProviderUnbound | None = None,
3319-
completer: CompleterUnbound | None = None,
3319+
choices_provider: ChoicesProviderUnbound[CmdOrSet] | None = None,
3320+
completer: CompleterUnbound[CmdOrSet] | None = None,
33203321
parser: argparse.ArgumentParser | None = None,
33213322
) -> str:
33223323
"""Read a line of input with optional completion and history.

cmd2/command_definition.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from .utils import Settable
1919

2020
if TYPE_CHECKING: # pragma: no cover
21-
import cmd2
21+
from .cmd2 import Cmd
2222

2323
#: Callable signature for a basic command function
2424
#: Further refinements are needed to define the input parameters
@@ -92,13 +92,13 @@ def __init__(self) -> None:
9292
This will be set when the CommandSet is registered and it should be
9393
accessed by child classes using the self._cmd property.
9494
"""
95-
self.__cmd_internal: cmd2.Cmd | None = None
95+
self.__cmd_internal: Cmd | None = None
9696

9797
self._settables: dict[str, Settable] = {}
9898
self._settable_prefix = self.__class__.__name__
9999

100100
@property
101-
def _cmd(self) -> 'cmd2.Cmd':
101+
def _cmd(self) -> 'Cmd':
102102
"""Property for child classes to access self.__cmd_internal.
103103
104104
Using this property ensures that self.__cmd_internal has been set
@@ -122,7 +122,7 @@ def _cmd(self) -> CustomCmdApp:
122122
raise CommandSetRegistrationError('This CommandSet is not registered')
123123
return self.__cmd_internal
124124

125-
def on_register(self, cmd: 'cmd2.Cmd') -> None:
125+
def on_register(self, cmd: 'Cmd') -> None:
126126
"""First step to registering a CommandSet, called by cmd2.Cmd.
127127
128128
The commands defined in this class have not been added to the CLI object at this point.

cmd2/completion.py

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,23 @@
33
import re
44
import sys
55
from collections.abc import (
6-
Callable,
76
Collection,
87
Iterable,
98
Iterator,
10-
Mapping,
119
Sequence,
1210
)
1311
from dataclasses import (
1412
dataclass,
1513
field,
1614
)
1715
from typing import (
18-
TYPE_CHECKING,
1916
Any,
20-
TypeAlias,
2117
cast,
2218
overload,
2319
)
2420

2521
from . import string_utils as su
2622

27-
if TYPE_CHECKING: # pragma: no cover
28-
from .cmd2 import Cmd
29-
from .command_definition import CommandSet
30-
3123
if sys.version_info >= (3, 11):
3224
from typing import Self
3325
else:
@@ -36,7 +28,6 @@
3628
from rich.protocol import is_renderable
3729

3830
from . import rich_utils as ru
39-
from . import utils
4031

4132
# Regular expression to identify strings which we should sort numerically
4233
NUMERIC_RE = re.compile(
@@ -151,6 +142,8 @@ class CompletionResultsBase:
151142

152143
def __post_init__(self) -> None:
153144
"""Finalize the object after initialization."""
145+
from . import utils
146+
154147
unique_items = utils.remove_duplicates(self.items)
155148
if not self.is_sorted:
156149
if all_display_numeric(unique_items):
@@ -264,49 +257,3 @@ class Completions(CompletionResultsBase):
264257
def all_display_numeric(items: Collection[CompletionItem]) -> bool:
265258
"""Return True if items is non-empty and every item.display_plain value is a numeric string."""
266259
return bool(items) and all(NUMERIC_RE.match(item.display_plain) for item in items)
267-
268-
269-
#############################################
270-
# choices_provider function types
271-
#############################################
272-
273-
# Represents the parsed tokens from argparse during completion
274-
ArgTokens: TypeAlias = Mapping[str, Sequence[str]]
275-
276-
# Unbound choices_provider function types used by argparse-based completion.
277-
# These expect a Cmd or CommandSet instance as the first argument.
278-
ChoicesProviderUnbound: TypeAlias = (
279-
# Basic: (self) -> Choices
280-
Callable[["Cmd"], Choices]
281-
| Callable[["CommandSet"], Choices]
282-
|
283-
# Context-aware: (self, arg_tokens) -> Choices
284-
Callable[["Cmd", ArgTokens], Choices]
285-
| Callable[["CommandSet", ArgTokens], Choices]
286-
)
287-
288-
#############################################
289-
# completer function types
290-
#############################################
291-
292-
# Unbound completer function types used by argparse-based completion.
293-
# These expect a Cmd or CommandSet instance as the first argument.
294-
CompleterUnbound: TypeAlias = (
295-
# Basic: (self, text, line, begidx, endidx) -> Completions
296-
Callable[["Cmd", str, str, int, int], Completions]
297-
| Callable[["CommandSet", str, str, int, int], Completions]
298-
|
299-
# Context-aware: (self, text, line, begidx, endidx, arg_tokens) -> Completions
300-
Callable[["Cmd", str, str, int, int, ArgTokens], Completions]
301-
| Callable[["CommandSet", str, str, int, int, ArgTokens], Completions]
302-
)
303-
304-
# A bound completer used internally by cmd2 for basic completion logic.
305-
# The 'self' argument is already tied to an instance and is omitted.
306-
# Format: (text, line, begidx, endidx) -> Completions
307-
CompleterBound: TypeAlias = Callable[[str, str, int, int], Completions]
308-
309-
# Represents a type that can be matched against when completing.
310-
# Strings are matched directly while CompletionItems are matched
311-
# against their 'text' member.
312-
Matchable: TypeAlias = str | CompletionItem

0 commit comments

Comments
 (0)