Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ cmd2/py_bridge.py @kmvanbrunt
cmd2/rich_utils.py @kmvanbrunt
cmd2/string_utils.py @kmvanbrunt
cmd2/styles.py @tleonhardt @kmvanbrunt
cmd2/types.py @tleonhardt @kmvanbrunt
cmd2/utils.py @tleonhardt @kmvanbrunt

# Documentation
Expand Down
20 changes: 10 additions & 10 deletions cmd2/argparse_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
See the header of argparse_custom.py for instructions on how to use these features.
"""

from __future__ import annotations
Comment thread
tleonhardt marked this conversation as resolved.
Outdated

import argparse
import dataclasses
import inspect
Expand All @@ -22,33 +24,31 @@
cast,
)

from rich.text import Text

from .constants import INFINITY
from .rich_utils import Cmd2GeneralConsole

if TYPE_CHECKING: # pragma: no cover
from .cmd2 import Cmd

from rich.box import SIMPLE_HEAD
from rich.table import (
Column,
Table,
)
from rich.text import Text

from .argparse_custom import (
ChoicesCallable,
generate_range_error,
)
from .command_definition import CommandSet
from .completion import (
CompletionItem,
Completions,
all_display_numeric,
)
from .constants import INFINITY
from .exceptions import CompletionError
from .rich_utils import Cmd2GeneralConsole
from .styles import Cmd2Style

if TYPE_CHECKING: # pragma: no cover
from .cmd2 import Cmd
from .command_definition import CommandSet

# If no table header is supplied, then this will be used instead
DEFAULT_TABLE_HEADER: Sequence[str | Column] = ['Description']

Expand Down Expand Up @@ -166,7 +166,7 @@ class ArgparseCompleter:
def __init__(
self,
parser: argparse.ArgumentParser,
cmd2_app: 'Cmd',
cmd2_app: Cmd,
*,
parent_tokens: Mapping[str, MutableSequence[str]] | None = None,
) -> None:
Expand Down
35 changes: 19 additions & 16 deletions cmd2/argparse_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ def get_choices(self) -> Choices:
sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser` for more details.
"""

from __future__ import annotations

import argparse
import re
import sys
Expand Down Expand Up @@ -294,13 +296,14 @@ def get_choices(self) -> Choices:

from . import constants
from . import rich_utils as ru
from .completion import (
from .completion import CompletionItem
from .rich_utils import Cmd2RichArgparseConsole
from .styles import Cmd2Style
from .types import (
ChoicesProviderUnbound,
CmdOrSet,
CompleterUnbound,
CompletionItem,
)
from .rich_utils import Cmd2RichArgparseConsole
from .styles import Cmd2Style

if TYPE_CHECKING: # pragma: no cover
from .argparse_completer import ArgparseCompleter
Expand Down Expand Up @@ -384,7 +387,7 @@ class ChoicesCallable:
def __init__(
self,
is_completer: bool,
to_call: ChoicesProviderUnbound | CompleterUnbound,
to_call: ChoicesProviderUnbound[CmdOrSet] | CompleterUnbound[CmdOrSet],
) -> None:
"""Initialize the ChoiceCallable instance.

Expand All @@ -396,18 +399,18 @@ def __init__(
self.to_call = to_call

@property
def choices_provider(self) -> ChoicesProviderUnbound:
def choices_provider(self) -> ChoicesProviderUnbound[CmdOrSet]:
"""Retreive the internal choices_provider function."""
if self.is_completer:
raise AttributeError("This instance is configured as a completer, not a choices_provider")
return cast(ChoicesProviderUnbound, self.to_call)
return cast(ChoicesProviderUnbound[CmdOrSet], self.to_call)

@property
def completer(self) -> CompleterUnbound:
def completer(self) -> CompleterUnbound[CmdOrSet]:
"""Retreive the internal completer function."""
if not self.is_completer:
raise AttributeError("This instance is configured as a choices_provider, not a completer")
return cast(CompleterUnbound, self.to_call)
return cast(CompleterUnbound[CmdOrSet], self.to_call)


############################################################################################################
Expand Down Expand Up @@ -476,7 +479,7 @@ def _action_set_choices_callable(self: argparse.Action, choices_callable: Choice

def _action_set_choices_provider(
self: argparse.Action,
choices_provider: ChoicesProviderUnbound,
choices_provider: ChoicesProviderUnbound[CmdOrSet],
) -> None:
"""Set choices_provider of an argparse Action.

Expand All @@ -496,7 +499,7 @@ def _action_set_choices_provider(

def _action_set_completer(
self: argparse.Action,
completer: CompleterUnbound,
completer: CompleterUnbound[CmdOrSet],
) -> None:
"""Set completer of an argparse Action.

Expand Down Expand Up @@ -694,8 +697,8 @@ def _add_argument_wrapper(
self: argparse._ActionsContainer,
*args: Any,
nargs: int | str | tuple[int] | tuple[int, int] | tuple[int, float] | None = None,
choices_provider: ChoicesProviderUnbound | None = None,
completer: CompleterUnbound | None = None,
choices_provider: ChoicesProviderUnbound[CmdOrSet] | None = None,
completer: CompleterUnbound[CmdOrSet] | None = None,
suppress_tab_hint: bool = False,
table_header: Sequence[str | Column] | None = None,
**kwargs: Any,
Expand Down Expand Up @@ -883,7 +886,7 @@ def _match_argument_wrapper(self: argparse.ArgumentParser, action: argparse.Acti
ATTR_AP_COMPLETER_TYPE = 'ap_completer_type'


def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> type['ArgparseCompleter'] | None: # noqa: N802
def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> type[ArgparseCompleter] | None: # noqa: N802
"""Get the ap_completer_type attribute of an argparse ArgumentParser.

This function is added by cmd2 as a method called ``get_ap_completer_type()`` to ``argparse.ArgumentParser`` class.
Expand All @@ -899,7 +902,7 @@ def _ArgumentParser_get_ap_completer_type(self: argparse.ArgumentParser) -> type
setattr(argparse.ArgumentParser, 'get_ap_completer_type', _ArgumentParser_get_ap_completer_type)


def _ArgumentParser_set_ap_completer_type(self: argparse.ArgumentParser, ap_completer_type: type['ArgparseCompleter']) -> None: # noqa: N802
def _ArgumentParser_set_ap_completer_type(self: argparse.ArgumentParser, ap_completer_type: type[ArgparseCompleter]) -> None: # noqa: N802
"""Set the ap_completer_type attribute of an argparse ArgumentParser.

This function is added by cmd2 as a method called ``set_ap_completer_type()`` to ``argparse.ArgumentParser`` class.
Expand Down Expand Up @@ -1185,7 +1188,7 @@ def __init__(
suggest_on_error: bool = False,
color: bool = False,
*,
ap_completer_type: type['ArgparseCompleter'] | None = None,
ap_completer_type: type[ArgparseCompleter] | None = None,
) -> None:
"""Initialize the Cmd2ArgumentParser instance, a custom ArgumentParser added by cmd2.

Expand Down
2 changes: 2 additions & 0 deletions cmd2/clipboard.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Module provides basic ability to copy from and paste to the clipboard/pastebuffer."""

from __future__ import annotations

import typing

import pyperclip # type: ignore[import-untyped]
Expand Down
65 changes: 33 additions & 32 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@
Documentation: https://cmd2.readthedocs.io/
"""

# This module has many imports, quite a few of which are only
# infrequently utilized. To reduce the initial overhead of
# import this module, many of these imports are lazy-loaded
# i.e. we only import the module when we use it.
from __future__ import annotations

import argparse
import contextlib
import copy
Expand Down Expand Up @@ -56,14 +54,13 @@
dataclass,
field,
)
from types import FrameType
from typing import (
IO,
TYPE_CHECKING,
Any,
TextIO,
TypeAlias,
TypeVar,
Union,
cast,
)

Expand Down Expand Up @@ -96,7 +93,6 @@
)
from . import rich_utils as ru
from . import string_utils as su
from .argparse_custom import Cmd2ArgumentParser
from .clipboard import (
get_paste_buffer,
write_to_paste_buffer,
Expand All @@ -107,12 +103,8 @@
)
from .completion import (
Choices,
ChoicesProviderUnbound,
CompleterBound,
CompleterUnbound,
CompletionItem,
Completions,
Matchable,
)
from .constants import (
CLASS_ATTR_DEFAULT_HELP_CATEGORY,
Expand All @@ -121,7 +113,6 @@
HELP_FUNC_PREFIX,
)
from .decorators import (
CommandParent,
as_subcommand_to,
with_argparser,
)
Expand Down Expand Up @@ -196,6 +187,24 @@ def __init__(self, msg: str = '') -> None:
suggest_similar,
)

if TYPE_CHECKING: # pragma: no cover
StaticArgParseBuilder = staticmethod[[], argparse.ArgumentParser]
ClassArgParseBuilder = classmethod['Cmd' | CommandSet, [], argparse.ArgumentParser]
from types import FrameType

from .argparse_custom import Cmd2ArgumentParser
from .types import (
ChoicesProviderUnbound,
CmdOrSet,
CompleterBound,
CompleterUnbound,
Matchable,
)

else:
StaticArgParseBuilder = staticmethod
ClassArgParseBuilder = classmethod


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


if TYPE_CHECKING: # pragma: no cover
StaticArgParseBuilder = staticmethod[[], argparse.ArgumentParser]
ClassArgParseBuilder = classmethod['Cmd' | CommandSet, [], argparse.ArgumentParser]
else:
StaticArgParseBuilder = staticmethod
ClassArgParseBuilder = classmethod


class _CommandParsers:
"""Create and store all command method argument parsers for a given Cmd instance.

Parser creation and retrieval are accomplished through the get() method.
"""

def __init__(self, cmd: 'Cmd') -> None:
def __init__(self, cmd: Cmd) -> None:
self._cmd = cmd

# Keyed by the fully qualified method names. This is more reliable than
Expand Down Expand Up @@ -840,7 +841,7 @@ def register_command_set(self, cmdset: CommandSet) -> None:

def _build_parser(
self,
parent: CommandParent,
parent: CmdOrSet,
parser_builder: argparse.ArgumentParser
| Callable[[], argparse.ArgumentParser]
| StaticArgParseBuilder
Expand All @@ -849,7 +850,7 @@ def _build_parser(
) -> argparse.ArgumentParser:
"""Build argument parser for a command/subcommand.

:param parent: CommandParent object which owns the command using the parser.
:param parent: object which owns the command using the parser.
When parser_builder is a classmethod, this function passes
parent's class to it.
:param parser_builder: means used to build the parser
Expand Down Expand Up @@ -1003,7 +1004,7 @@ def check_parser_uninstallable(parser: argparse.ArgumentParser) -> None:
if command_parser is not None:
check_parser_uninstallable(command_parser)

def _register_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
def _register_subcommands(self, cmdset: CommandSet | Cmd) -> None:
"""Register subcommands with their base command.

:param cmdset: CommandSet or cmd2.Cmd subclass containing subcommands
Expand Down Expand Up @@ -1096,7 +1097,7 @@ def find_subcommand(

break

def _unregister_subcommands(self, cmdset: Union[CommandSet, 'Cmd']) -> None:
def _unregister_subcommands(self, cmdset: CommandSet | Cmd) -> None:
"""Unregister subcommands from their base command.

:param cmdset: CommandSet containing subcommands
Expand Down Expand Up @@ -2193,8 +2194,8 @@ def _determine_ap_completer_type(parser: argparse.ArgumentParser) -> type[argpar
:param parser: the parser to examine
:return: type of ArgparseCompleter
"""
Completer = type[argparse_completer.ArgparseCompleter] | None # noqa: N806
completer_type: Completer = parser.get_ap_completer_type() # type: ignore[attr-defined]
CompleterType: TypeAlias = type[argparse_completer.ArgparseCompleter] | None
completer_type: CompleterType = parser.get_ap_completer_type() # type: ignore[attr-defined]

if completer_type is None:
completer_type = argparse_completer.DEFAULT_AP_COMPLETER
Expand Down Expand Up @@ -3283,8 +3284,8 @@ def _resolve_completer(
self,
preserve_quotes: bool = False,
choices: Iterable[Any] | None = None,
choices_provider: ChoicesProviderUnbound | None = None,
completer: CompleterUnbound | None = None,
choices_provider: ChoicesProviderUnbound[CmdOrSet] | None = None,
completer: CompleterUnbound[CmdOrSet] | None = None,
parser: argparse.ArgumentParser | None = None,
) -> Completer:
"""Determine the appropriate completer based on provided arguments."""
Expand Down Expand Up @@ -3315,8 +3316,8 @@ def read_input(
history: Sequence[str] | None = None,
preserve_quotes: bool = False,
choices: Iterable[Any] | None = None,
choices_provider: ChoicesProviderUnbound | None = None,
completer: CompleterUnbound | None = None,
choices_provider: ChoicesProviderUnbound[CmdOrSet] | None = None,
completer: CompleterUnbound[CmdOrSet] | None = None,
parser: argparse.ArgumentParser | None = None,
) -> str:
"""Read a line of input with optional completion and history.
Expand Down Expand Up @@ -5659,7 +5660,7 @@ def register_cmdfinalization_hook(
def _resolve_func_self(
self,
cmd_support_func: Callable[..., Any],
cmd_self: Union[CommandSet, 'Cmd', None],
cmd_self: CommandSet | Cmd | None,
) -> object | None:
"""Attempt to resolve a candidate instance to pass as 'self'.

Expand Down
2 changes: 2 additions & 0 deletions cmd2/colors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Provides a convenient StrEnum for Rich color names."""

from __future__ import annotations

import sys

if sys.version_info >= (3, 11):
Expand Down
Loading
Loading