Skip to content

Commit bcf0466

Browse files
committed
Added "from __future__ import annotations" to each cmd2 module.
Fixed circular import between utils.py and completion.py.
1 parent e239bb5 commit bcf0466

20 files changed

+152
-93
lines changed

cmd2/argparse_completer.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
See the header of argparse_custom.py for instructions on how to use these features.
44
"""
55

6+
from __future__ import annotations
7+
68
import argparse
79
import dataclasses
810
import inspect
@@ -22,33 +24,31 @@
2224
cast,
2325
)
2426

25-
from rich.text import Text
26-
27-
from .constants import INFINITY
28-
from .rich_utils import Cmd2GeneralConsole
29-
30-
if TYPE_CHECKING: # pragma: no cover
31-
from .cmd2 import Cmd
32-
3327
from rich.box import SIMPLE_HEAD
3428
from rich.table import (
3529
Column,
3630
Table,
3731
)
32+
from rich.text import Text
3833

3934
from .argparse_custom import (
4035
ChoicesCallable,
4136
generate_range_error,
4237
)
43-
from .command_definition import CommandSet
4438
from .completion import (
4539
CompletionItem,
4640
Completions,
4741
all_display_numeric,
4842
)
43+
from .constants import INFINITY
4944
from .exceptions import CompletionError
45+
from .rich_utils import Cmd2GeneralConsole
5046
from .styles import Cmd2Style
5147

48+
if TYPE_CHECKING: # pragma: no cover
49+
from .cmd2 import Cmd
50+
from .command_definition import CommandSet
51+
5252
# If no table header is supplied, then this will be used instead
5353
DEFAULT_TABLE_HEADER: Sequence[str | Column] = ['Description']
5454

@@ -166,7 +166,7 @@ class ArgparseCompleter:
166166
def __init__(
167167
self,
168168
parser: argparse.ArgumentParser,
169-
cmd2_app: 'Cmd',
169+
cmd2_app: Cmd,
170170
*,
171171
parent_tokens: Mapping[str, MutableSequence[str]] | None = None,
172172
) -> None:

cmd2/argparse_custom.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ def get_choices(self) -> Choices:
261261
sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser` for more details.
262262
"""
263263

264+
from __future__ import annotations
265+
264266
import argparse
265267
import re
266268
import sys
@@ -884,7 +886,7 @@ def _match_argument_wrapper(self: argparse.ArgumentParser, action: argparse.Acti
884886
ATTR_AP_COMPLETER_TYPE = 'ap_completer_type'
885887

886888

887-
def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> type['ArgparseCompleter'] | None: # noqa: N802
889+
def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> type[ArgparseCompleter] | None: # noqa: N802
888890
"""Get the ap_completer_type attribute of an argparse ArgumentParser.
889891
890892
This function is added by cmd2 as a method called ``get_ap_completer_type()`` to ``argparse.ArgumentParser`` class.
@@ -900,7 +902,7 @@ def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> type
900902
setattr(argparse.ArgumentParser, 'get_ap_completer_type', _ArgumentParser_get_ap_completer_type)
901903

902904

903-
def _ArgumentParser_set_ap_completer_type(self: argparse.ArgumentParser, ap_completer_type: type['ArgparseCompleter']) -> None: # noqa: N802
905+
def _ArgumentParser_set_ap_completer_type(self: argparse.ArgumentParser, ap_completer_type: type[ArgparseCompleter]) -> None: # noqa: N802
904906
"""Set the ap_completer_type attribute of an argparse ArgumentParser.
905907
906908
This function is added by cmd2 as a method called ``set_ap_completer_type()`` to ``argparse.ArgumentParser`` class.
@@ -1186,7 +1188,7 @@ def __init__(
11861188
suggest_on_error: bool = False,
11871189
color: bool = False,
11881190
*,
1189-
ap_completer_type: type['ArgparseCompleter'] | None = None,
1191+
ap_completer_type: type[ArgparseCompleter] | None = None,
11901192
) -> None:
11911193
"""Initialize the Cmd2ArgumentParser instance, a custom ArgumentParser added by cmd2.
11921194

cmd2/clipboard.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Module provides basic ability to copy from and paste to the clipboard/pastebuffer."""
22

3+
from __future__ import annotations
4+
35
import typing
46

57
import pyperclip # type: ignore[import-untyped]

cmd2/cmd2.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@
2222
Documentation: https://cmd2.readthedocs.io/
2323
"""
2424

25-
# This module has many imports, quite a few of which are only
26-
# infrequently utilized. To reduce the initial overhead of
27-
# import this module, many of these imports are lazy-loaded
28-
# i.e. we only import the module when we use it.
25+
from __future__ import annotations
26+
2927
import argparse
3028
import contextlib
3129
import copy
@@ -56,14 +54,13 @@
5654
dataclass,
5755
field,
5856
)
59-
from types import FrameType
6057
from typing import (
6158
IO,
6259
TYPE_CHECKING,
6360
Any,
6461
TextIO,
62+
TypeAlias,
6563
TypeVar,
66-
Union,
6764
cast,
6865
)
6966

@@ -96,7 +93,6 @@
9693
)
9794
from . import rich_utils as ru
9895
from . import string_utils as su
99-
from .argparse_custom import Cmd2ArgumentParser
10096
from .clipboard import (
10197
get_paste_buffer,
10298
write_to_paste_buffer,
@@ -147,13 +143,6 @@
147143
RichPrintKwargs,
148144
)
149145
from .styles import Cmd2Style
150-
from .types import (
151-
ChoicesProviderUnbound,
152-
CmdOrSet,
153-
CompleterBound,
154-
CompleterUnbound,
155-
Matchable,
156-
)
157146

158147
with contextlib.suppress(ImportError):
159148
from IPython import start_ipython
@@ -198,6 +187,24 @@ def __init__(self, msg: str = '') -> None:
198187
suggest_similar,
199188
)
200189

190+
if TYPE_CHECKING: # pragma: no cover
191+
StaticArgParseBuilder = staticmethod[[], argparse.ArgumentParser]
192+
ClassArgParseBuilder = classmethod['Cmd' | CommandSet, [], argparse.ArgumentParser]
193+
from types import FrameType
194+
195+
from .argparse_custom import Cmd2ArgumentParser
196+
from .types import (
197+
ChoicesProviderUnbound,
198+
CmdOrSet,
199+
CompleterBound,
200+
CompleterUnbound,
201+
Matchable,
202+
)
203+
204+
else:
205+
StaticArgParseBuilder = staticmethod
206+
ClassArgParseBuilder = classmethod
207+
201208

202209
class _SavedCmd2Env:
203210
"""cmd2 environment settings that are backed up when entering an interactive Python shell."""
@@ -211,21 +218,13 @@ def __init__(self) -> None:
211218
DisabledCommand = namedtuple('DisabledCommand', ['command_function', 'help_function', 'completer_function']) # noqa: PYI024
212219

213220

214-
if TYPE_CHECKING: # pragma: no cover
215-
StaticArgParseBuilder = staticmethod[[], argparse.ArgumentParser]
216-
ClassArgParseBuilder = classmethod['Cmd' | CommandSet, [], argparse.ArgumentParser]
217-
else:
218-
StaticArgParseBuilder = staticmethod
219-
ClassArgParseBuilder = classmethod
220-
221-
222221
class _CommandParsers:
223222
"""Create and store all command method argument parsers for a given Cmd instance.
224223
225224
Parser creation and retrieval are accomplished through the get() method.
226225
"""
227226

228-
def __init__(self, cmd: 'Cmd') -> None:
227+
def __init__(self, cmd: Cmd) -> None:
229228
self._cmd = cmd
230229

231230
# Keyed by the fully qualified method names. This is more reliable than
@@ -1005,7 +1004,7 @@ def check_parser_uninstallable(parser: argparse.ArgumentParser) -> None:
10051004
if command_parser is not None:
10061005
check_parser_uninstallable(command_parser)
10071006

1008-
def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
1007+
def _register_subcommands(self, cmdset: CommandSet | Cmd) -> None:
10091008
"""Register subcommands with their base command.
10101009
10111010
:param cmdset: CommandSet or cmd2.Cmd subclass containing subcommands
@@ -1098,7 +1097,7 @@ def find_subcommand(
10981097

10991098
break
11001099

1101-
def _unregister_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
1100+
def _unregister_subcommands(self, cmdset: CommandSet | Cmd) -> None:
11021101
"""Unregister subcommands from their base command.
11031102
11041103
:param cmdset: CommandSet containing subcommands
@@ -2195,8 +2194,8 @@ def _determine_ap_completer_type(parser: argparse.ArgumentParser) -> type[argpar
21952194
:param parser: the parser to examine
21962195
:return: type of ArgparseCompleter
21972196
"""
2198-
Completer = type[argparse_completer.ArgparseCompleter] | None # noqa: N806
2199-
completer_type: Completer = parser.get_ap_completer_type() # type: ignore[attr-defined]
2197+
CompleterType: TypeAlias = type[argparse_completer.ArgparseCompleter] | None
2198+
completer_type: CompleterType = parser.get_ap_completer_type() # type: ignore[attr-defined]
22002199

22012200
if completer_type is None:
22022201
completer_type = argparse_completer.DEFAULT_AP_COMPLETER
@@ -5661,7 +5660,7 @@ def register_cmdfinalization_hook(
56615660
def _resolve_func_self(
56625661
self,
56635662
cmd_support_func: Callable[..., Any],
5664-
cmd_self: Union[CommandSet, 'Cmd', None],
5663+
cmd_self: CommandSet | Cmd | None,
56655664
) -> object | None:
56665665
"""Attempt to resolve a candidate instance to pass as 'self'.
56675666

cmd2/colors.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Provides a convenient StrEnum for Rich color names."""
22

3+
from __future__ import annotations
4+
35
import sys
46

57
if sys.version_info >= (3, 11):

cmd2/command_definition.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Supports the definition of commands in separate classes to be composed into cmd2.Cmd."""
22

3+
from __future__ import annotations
4+
35
from collections.abc import (
46
Callable,
57
Mapping,
@@ -15,10 +17,10 @@
1517
COMMAND_FUNC_PREFIX,
1618
)
1719
from .exceptions import CommandSetRegistrationError
18-
from .utils import Settable
1920

2021
if TYPE_CHECKING: # pragma: no cover
2122
from .cmd2 import Cmd
23+
from .utils import Settable
2224

2325
#: Callable signature for a basic command function
2426
#: Further refinements are needed to define the input parameters
@@ -98,7 +100,7 @@ def __init__(self) -> None:
98100
self._settable_prefix = self.__class__.__name__
99101

100102
@property
101-
def _cmd(self) -> 'Cmd':
103+
def _cmd(self) -> Cmd:
102104
"""Property for child classes to access self.__cmd_internal.
103105
104106
Using this property ensures that self.__cmd_internal has been set
@@ -122,7 +124,7 @@ def _cmd(self) -> CustomCmdApp:
122124
raise CommandSetRegistrationError('This CommandSet is not registered')
123125
return self.__cmd_internal
124126

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

cmd2/completion.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
"""Provides classes and functions related to command-line completion."""
22

3+
from __future__ import annotations
4+
35
import re
46
import sys
5-
from collections.abc import (
6-
Collection,
7-
Iterable,
8-
Iterator,
9-
Sequence,
10-
)
117
from dataclasses import (
128
dataclass,
139
field,
1410
)
1511
from typing import (
12+
TYPE_CHECKING,
1613
Any,
1714
cast,
1815
overload,
@@ -28,7 +25,14 @@
2825
from rich.protocol import is_renderable
2926

3027
from . import rich_utils as ru
31-
from . import utils
28+
29+
if TYPE_CHECKING:
30+
from collections.abc import (
31+
Collection,
32+
Iterable,
33+
Iterator,
34+
Sequence,
35+
)
3236

3337
# Regular expression to identify strings which we should sort numerically
3438
NUMERIC_RE = re.compile(
@@ -143,6 +147,8 @@ class CompletionResultsBase:
143147

144148
def __post_init__(self) -> None:
145149
"""Finalize the object after initialization."""
150+
from . import utils
151+
146152
unique_items = utils.remove_duplicates(self.items)
147153
if not self.is_sorted:
148154
if all_display_numeric(unique_items):

cmd2/constants.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
"""Constants used throughout ``cmd2``."""
1+
"""Constants used throughout ``cmd2``.
22
3-
# Unless documented in https://cmd2.readthedocs.io/en/latest/api/index.html
4-
# nothing here should be considered part of the public API of this module
3+
Unless documented in https://cmd2.readthedocs.io/en/latest/api/index.html
4+
nothing here should be considered part of the public API of this module
5+
"""
6+
7+
from __future__ import annotations
58

69
INFINITY = float('inf')
710

cmd2/decorators.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Decorators for ``cmd2`` commands."""
22

3+
from __future__ import annotations
4+
35
import argparse
46
from collections.abc import (
57
Callable,
@@ -65,7 +67,7 @@ def cat_decorator(func: CommandFunc) -> CommandFunc:
6567
# in cmd2 command functions/callables. As long as the 2-ple of arguments we expect to be there can be
6668
# found we can swap out the statement with each decorator's specific parameters
6769
##########################
68-
def _parse_positionals(args: tuple[Any, ...]) -> tuple['Cmd', Statement | str]:
70+
def _parse_positionals(args: tuple[Any, ...]) -> tuple[Cmd, Statement | str]:
6971
"""Inspect the positional arguments until the cmd2.Cmd argument is found.
7072
7173
Assumes that we will find cmd2.Cmd followed by the command statement object or string.

cmd2/exceptions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Custom exceptions for cmd2."""
22

3+
from __future__ import annotations
4+
35
from typing import Any
46

57
############################################################################################################

0 commit comments

Comments
 (0)