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 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
- while True:
3601
- try:
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
- except StopIteration:
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
- while True:
3628
- try:
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
- except StopIteration:
3636
- break
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 notation
5549
- signature = inspect.signature(func)
5550
- if signature.return_annotation is not None:
5551
- raise TypeError(f"{func.__name__} must declare return a return type of 'None'")
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
- signature = inspect.signature(func)
5568
- _, param = next(iter(signature.parameters.items()))
5569
- if param.annotation != plugin.PostparsingData:
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 signature.return_annotation != plugin.PostparsingData:
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
- paramname = next(iter(signature.parameters.keys()))
5591
- param = signature.parameters[paramname]
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 signature.return_annotation == signature.empty:
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 signature.return_annotation != data_type:
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
- signature = inspect.signature(func)
5619
- _, param = next(iter(signature.parameters.items()))
5620
- if param.annotation != plugin.CommandFinalizationData:
5621
- raise TypeError(f"{func.__name__} must have one parameter declared with type {plugin.CommandFinalizationData}")
5622
- if signature.return_annotation != plugin.CommandFinalizationData:
5623
- raise TypeError("{func.__name__} must declare return a return type of {plugin.CommandFinalizationData}")
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
- SequenceMatcher,
19
- )
20
- from enum import (
21
- Enum,
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmd2
3
- Version: 2.6.0
3
+ Version: 2.6.1
4
4
  Summary: cmd2 - quickly build feature-rich and user-friendly interactive command line applications in Python
5
5
  Author: cmd2 Contributors
6
6
  License: The MIT License (MIT)
@@ -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=7U-uTdWSMumFjvDIYUvd5V0Bg87Av9V4yG-aODUTr-Q,261285
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=vY4JdnTbKJ2nhkDva_4Jiw1RY7eOoqkcinP6sVhgLyU,49486
20
- cmd2-2.6.0.dist-info/licenses/LICENSE,sha256=QXrW0Z0merk9mncyUkn-sgRxhT8_o1dL5HEaBNH47Q4,1099
21
- cmd2-2.6.0.dist-info/METADATA,sha256=k_YfbO5kXJByal_dPlrs6B3dbtKI1DizrDoX8Qd5xzc,17133
22
- cmd2-2.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- cmd2-2.6.0.dist-info/top_level.txt,sha256=gJbOJmyrARwLhm5diXAtzlNQdxbDZ8iRJ8HJi65_5hg,5
24
- cmd2-2.6.0.dist-info/RECORD,,
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