cmd2 2.6.0__py3-none-any.whl → 2.6.1__py3-none-any.whl
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.
- cmd2/cmd2.py +39 -33
- cmd2/utils.py +30 -23
- {cmd2-2.6.0.dist-info → cmd2-2.6.1.dist-info}/METADATA +1 -1
- {cmd2-2.6.0.dist-info → cmd2-2.6.1.dist-info}/RECORD +7 -7
- {cmd2-2.6.0.dist-info → cmd2-2.6.1.dist-info}/WHEEL +0 -0
- {cmd2-2.6.0.dist-info → cmd2-2.6.1.dist-info}/licenses/LICENSE +0 -0
- {cmd2-2.6.0.dist-info → cmd2-2.6.1.dist-info}/top_level.txt +0 -0
cmd2/cmd2.py
CHANGED
@@ -144,6 +144,7 @@ from .table_creator import (
|
|
144
144
|
from .utils import (
|
145
145
|
Settable,
|
146
146
|
get_defining_class,
|
147
|
+
get_types,
|
147
148
|
strip_doc_annotations,
|
148
149
|
suggest_similar,
|
149
150
|
)
|
@@ -3597,8 +3598,8 @@ class Cmd(cmd.Cmd):
|
|
3597
3598
|
max_arg_num = 0
|
3598
3599
|
arg_nums = set()
|
3599
3600
|
|
3600
|
-
|
3601
|
-
|
3601
|
+
try:
|
3602
|
+
while True:
|
3602
3603
|
cur_match = normal_matches.__next__()
|
3603
3604
|
|
3604
3605
|
# Get the number string between the braces
|
@@ -3612,9 +3613,8 @@ class Cmd(cmd.Cmd):
|
|
3612
3613
|
max_arg_num = max(max_arg_num, cur_num)
|
3613
3614
|
|
3614
3615
|
arg_list.append(MacroArg(start_index=cur_match.start(), number_str=cur_num_str, is_escaped=False))
|
3615
|
-
|
3616
|
-
|
3617
|
-
break
|
3616
|
+
except StopIteration:
|
3617
|
+
pass
|
3618
3618
|
|
3619
3619
|
# Make sure the argument numbers are continuous
|
3620
3620
|
if len(arg_nums) != max_arg_num:
|
@@ -3624,16 +3624,16 @@ class Cmd(cmd.Cmd):
|
|
3624
3624
|
# Find all escaped arguments
|
3625
3625
|
escaped_matches = re.finditer(MacroArg.macro_escaped_arg_pattern, value)
|
3626
3626
|
|
3627
|
-
|
3628
|
-
|
3627
|
+
try:
|
3628
|
+
while True:
|
3629
3629
|
cur_match = escaped_matches.__next__()
|
3630
3630
|
|
3631
3631
|
# Get the number string between the braces
|
3632
3632
|
cur_num_str = re.findall(MacroArg.digit_pattern, cur_match.group())[0]
|
3633
3633
|
|
3634
3634
|
arg_list.append(MacroArg(start_index=cur_match.start(), number_str=cur_num_str, is_escaped=True))
|
3635
|
-
|
3636
|
-
|
3635
|
+
except StopIteration:
|
3636
|
+
pass
|
3637
3637
|
|
3638
3638
|
# Set the macro
|
3639
3639
|
result = "overwritten" if args.name in self.macros else "created"
|
@@ -5545,10 +5545,10 @@ class Cmd(cmd.Cmd):
|
|
5545
5545
|
def _validate_prepostloop_callable(cls, func: Callable[[], None]) -> None:
|
5546
5546
|
"""Check parameter and return types for preloop and postloop hooks."""
|
5547
5547
|
cls._validate_callable_param_count(func, 0)
|
5548
|
-
# make sure there is no return
|
5549
|
-
|
5550
|
-
if
|
5551
|
-
raise TypeError(f"{func.__name__} must
|
5548
|
+
# make sure there is no return annotation or the return is specified as None
|
5549
|
+
_, ret_ann = get_types(func)
|
5550
|
+
if ret_ann is not None:
|
5551
|
+
raise TypeError(f"{func.__name__} must have a return type of 'None', got: {ret_ann}")
|
5552
5552
|
|
5553
5553
|
def register_preloop_hook(self, func: Callable[[], None]) -> None:
|
5554
5554
|
"""Register a function to be called at the beginning of the command loop."""
|
@@ -5564,11 +5564,13 @@ class Cmd(cmd.Cmd):
|
|
5564
5564
|
def _validate_postparsing_callable(cls, func: Callable[[plugin.PostparsingData], plugin.PostparsingData]) -> None:
|
5565
5565
|
"""Check parameter and return types for postparsing hooks."""
|
5566
5566
|
cls._validate_callable_param_count(cast(Callable[..., Any], func), 1)
|
5567
|
-
|
5568
|
-
|
5569
|
-
|
5567
|
+
type_hints, ret_ann = get_types(func)
|
5568
|
+
if not type_hints:
|
5569
|
+
raise TypeError(f"{func.__name__} parameter is missing a type hint, expected: 'cmd2.plugin.PostparsingData'")
|
5570
|
+
par_ann = next(iter(type_hints.values()))
|
5571
|
+
if par_ann != plugin.PostparsingData:
|
5570
5572
|
raise TypeError(f"{func.__name__} must have one parameter declared with type 'cmd2.plugin.PostparsingData'")
|
5571
|
-
if
|
5573
|
+
if ret_ann != plugin.PostparsingData:
|
5572
5574
|
raise TypeError(f"{func.__name__} must declare return a return type of 'cmd2.plugin.PostparsingData'")
|
5573
5575
|
|
5574
5576
|
def register_postparsing_hook(self, func: Callable[[plugin.PostparsingData], plugin.PostparsingData]) -> None:
|
@@ -5583,21 +5585,21 @@ class Cmd(cmd.Cmd):
|
|
5583
5585
|
cls, func: Callable[[CommandDataType], CommandDataType], data_type: type[CommandDataType]
|
5584
5586
|
) -> None:
|
5585
5587
|
"""Check parameter and return types for pre and post command hooks."""
|
5586
|
-
signature = inspect.signature(func)
|
5587
5588
|
# validate that the callable has the right number of parameters
|
5588
5589
|
cls._validate_callable_param_count(cast(Callable[..., Any], func), 1)
|
5590
|
+
|
5591
|
+
type_hints, ret_ann = get_types(func)
|
5592
|
+
if not type_hints:
|
5593
|
+
raise TypeError(f"{func.__name__} parameter is missing a type hint, expected: {data_type}")
|
5594
|
+
param_name, par_ann = next(iter(type_hints.items()))
|
5589
5595
|
# validate the parameter has the right annotation
|
5590
|
-
|
5591
|
-
|
5592
|
-
if param.annotation != data_type:
|
5593
|
-
raise TypeError(f'argument 1 of {func.__name__} has incompatible type {param.annotation}, expected {data_type}')
|
5596
|
+
if par_ann != data_type:
|
5597
|
+
raise TypeError(f'argument 1 of {func.__name__} has incompatible type {par_ann}, expected {data_type}')
|
5594
5598
|
# validate the return value has the right annotation
|
5595
|
-
if
|
5599
|
+
if ret_ann is None:
|
5596
5600
|
raise TypeError(f'{func.__name__} does not have a declared return type, expected {data_type}')
|
5597
|
-
if
|
5598
|
-
raise TypeError(
|
5599
|
-
f'{func.__name__} has incompatible return type {signature.return_annotation}, expected {data_type}'
|
5600
|
-
)
|
5601
|
+
if ret_ann != data_type:
|
5602
|
+
raise TypeError(f'{func.__name__} has incompatible return type {ret_ann}, expected {data_type}')
|
5601
5603
|
|
5602
5604
|
def register_precmd_hook(self, func: Callable[[plugin.PrecommandData], plugin.PrecommandData]) -> None:
|
5603
5605
|
"""Register a hook to be called before the command function."""
|
@@ -5615,12 +5617,16 @@ class Cmd(cmd.Cmd):
|
|
5615
5617
|
) -> None:
|
5616
5618
|
"""Check parameter and return types for command finalization hooks."""
|
5617
5619
|
cls._validate_callable_param_count(func, 1)
|
5618
|
-
|
5619
|
-
|
5620
|
-
|
5621
|
-
|
5622
|
-
if
|
5623
|
-
raise TypeError(
|
5620
|
+
type_hints, ret_ann = get_types(func)
|
5621
|
+
if not type_hints:
|
5622
|
+
raise TypeError(f"{func.__name__} parameter is missing a type hint, expected: {plugin.CommandFinalizationData}")
|
5623
|
+
_, par_ann = next(iter(type_hints.items()))
|
5624
|
+
if par_ann != plugin.CommandFinalizationData:
|
5625
|
+
raise TypeError(
|
5626
|
+
f"{func.__name__} must have one parameter declared with type {plugin.CommandFinalizationData}, got: {par_ann}"
|
5627
|
+
)
|
5628
|
+
if ret_ann != plugin.CommandFinalizationData:
|
5629
|
+
raise TypeError(f"{func.__name__} must declare return a return type of {plugin.CommandFinalizationData}")
|
5624
5630
|
|
5625
5631
|
def register_cmdfinalization_hook(
|
5626
5632
|
self, func: Callable[[plugin.CommandFinalizationData], plugin.CommandFinalizationData]
|
cmd2/utils.py
CHANGED
@@ -14,29 +14,12 @@ import sys
|
|
14
14
|
import threading
|
15
15
|
import unicodedata
|
16
16
|
from collections.abc import Callable, Iterable
|
17
|
-
from difflib import
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
from typing import (
|
24
|
-
TYPE_CHECKING,
|
25
|
-
Any,
|
26
|
-
Optional,
|
27
|
-
TextIO,
|
28
|
-
TypeVar,
|
29
|
-
Union,
|
30
|
-
cast,
|
31
|
-
)
|
32
|
-
|
33
|
-
from . import (
|
34
|
-
constants,
|
35
|
-
)
|
36
|
-
from .argparse_custom import (
|
37
|
-
ChoicesProviderFunc,
|
38
|
-
CompleterFunc,
|
39
|
-
)
|
17
|
+
from difflib import SequenceMatcher
|
18
|
+
from enum import Enum
|
19
|
+
from typing import TYPE_CHECKING, Any, Optional, TextIO, TypeVar, Union, cast, get_type_hints
|
20
|
+
|
21
|
+
from . import constants
|
22
|
+
from .argparse_custom import ChoicesProviderFunc, CompleterFunc
|
40
23
|
|
41
24
|
if TYPE_CHECKING: # pragma: no cover
|
42
25
|
import cmd2 # noqa: F401
|
@@ -1261,3 +1244,27 @@ def suggest_similar(
|
|
1261
1244
|
best_simil = simil
|
1262
1245
|
proposed_command = each
|
1263
1246
|
return proposed_command
|
1247
|
+
|
1248
|
+
|
1249
|
+
def get_types(func_or_method: Callable[..., Any]) -> tuple[dict[str, Any], Any]:
|
1250
|
+
"""Use typing.get_type_hints() to extract type hints for parameters and return value.
|
1251
|
+
|
1252
|
+
This exists because the inspect module doesn't have a safe way of doing this that works
|
1253
|
+
both with and without importing annotations from __future__ until Python 3.10.
|
1254
|
+
|
1255
|
+
TODO: Once cmd2 only supports Python 3.10+, change to use inspect.get_annotations(eval_str=True)
|
1256
|
+
|
1257
|
+
:param func_or_method: Function or method to return the type hints for
|
1258
|
+
:return tuple with first element being dictionary mapping param names to type hints
|
1259
|
+
and second element being return type hint, unspecified, returns None
|
1260
|
+
"""
|
1261
|
+
try:
|
1262
|
+
type_hints = get_type_hints(func_or_method) # Get dictionary of type hints
|
1263
|
+
except TypeError as exc:
|
1264
|
+
raise ValueError("Argument passed to get_types should be a function or method") from exc
|
1265
|
+
ret_ann = type_hints.pop('return', None) # Pop off the return annotation if it exists
|
1266
|
+
if inspect.ismethod(func_or_method):
|
1267
|
+
type_hints.pop('self', None) # Pop off `self` hint for methods
|
1268
|
+
if ret_ann is type(None):
|
1269
|
+
ret_ann = None # Simplify logic to just return None instead of NoneType
|
1270
|
+
return type_hints, ret_ann
|
@@ -3,7 +3,7 @@ cmd2/ansi.py,sha256=XwBvFnB51LYYjIpfEge6mcr5X63dUxiV0aJlOOJ_2tQ,31965
|
|
3
3
|
cmd2/argparse_completer.py,sha256=F_5hiX9fYpEhV42mz7v7qeS7KuInAS6xU6KekLxYzsQ,35863
|
4
4
|
cmd2/argparse_custom.py,sha256=Q7N-2TSctfjn3TEZezQNaT5y0w8sdGpmUA5_DCPa_Pk,59131
|
5
5
|
cmd2/clipboard.py,sha256=HLZWY-W3mkpF_OqQet-F8pS3rlXpiE5vxUyPV_yHvIU,507
|
6
|
-
cmd2/cmd2.py,sha256=
|
6
|
+
cmd2/cmd2.py,sha256=y2gqtQaHG4Oy5MB1VTCX6Tlgvji6NGuypS9x2dPFrUI,261541
|
7
7
|
cmd2/command_definition.py,sha256=_wF39nig5MP0grSOZ1hFehP5fCGusUNHMn9PIs6pmcU,7648
|
8
8
|
cmd2/constants.py,sha256=duEqGhhvdUV7AYxE8VGZ4wyFBcN2y2bLPd5iefW4tSU,1943
|
9
9
|
cmd2/decorators.py,sha256=9Jiml8HGc-UyUtWTns3TwW8ZHte4NchfGgHe-_1CcDM,20075
|
@@ -16,9 +16,9 @@ cmd2/py_bridge.py,sha256=TKl2d7sgptDdccuO1PWG8m83Nww2bGCzsY4_wqjKTjg,4935
|
|
16
16
|
cmd2/rl_utils.py,sha256=sWapqwDR_GvOgy5NJ7c-i6nUU4dqpKpGQ8-Olwi3tuU,11335
|
17
17
|
cmd2/table_creator.py,sha256=nF3VEgGHvfhHBh_yoMAiV1WaDdR8hIareIAogzIPq5M,47177
|
18
18
|
cmd2/transcript.py,sha256=Ws0lW_oBFxCQCKSwvnDUS1x3ya163YXnV-O5aTU0z7k,9207
|
19
|
-
cmd2/utils.py,sha256=
|
20
|
-
cmd2-2.6.
|
21
|
-
cmd2-2.6.
|
22
|
-
cmd2-2.6.
|
23
|
-
cmd2-2.6.
|
24
|
-
cmd2-2.6.
|
19
|
+
cmd2/utils.py,sha256=NqDIkY4LiAlnv_Un7MFHxszoH2KgEWQ7NJOwQurIn_E,50698
|
20
|
+
cmd2-2.6.1.dist-info/licenses/LICENSE,sha256=QXrW0Z0merk9mncyUkn-sgRxhT8_o1dL5HEaBNH47Q4,1099
|
21
|
+
cmd2-2.6.1.dist-info/METADATA,sha256=JNWrp7MjHaU-kJjMoklgq_EyEHbkSd4QscQs5x3YswA,17133
|
22
|
+
cmd2-2.6.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
23
|
+
cmd2-2.6.1.dist-info/top_level.txt,sha256=gJbOJmyrARwLhm5diXAtzlNQdxbDZ8iRJ8HJi65_5hg,5
|
24
|
+
cmd2-2.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|