cmd2 3.1.0__py3-none-any.whl → 3.1.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/argparse_custom.py CHANGED
@@ -258,14 +258,11 @@ sub-parser from a sub-parsers group. See _SubParsersAction_remove_parser` for mo
258
258
  import argparse
259
259
  import re
260
260
  import sys
261
- from argparse import (
262
- ONE_OR_MORE,
263
- ZERO_OR_MORE,
264
- ArgumentError,
265
- )
261
+ from argparse import ArgumentError
266
262
  from collections.abc import (
267
263
  Callable,
268
264
  Iterable,
265
+ Iterator,
269
266
  Sequence,
270
267
  )
271
268
  from gettext import gettext
@@ -293,6 +290,7 @@ from rich_argparse import (
293
290
  RawTextRichHelpFormatter,
294
291
  RichHelpFormatter,
295
292
  )
293
+ from typing_extensions import Self
296
294
 
297
295
  from . import constants
298
296
  from . import rich_utils as ru
@@ -380,7 +378,7 @@ class CompletionItem(str): # noqa: SLOT000
380
378
  See header of this file for more information
381
379
  """
382
380
 
383
- def __new__(cls, value: object, *_args: Any, **_kwargs: Any) -> 'CompletionItem':
381
+ def __new__(cls, value: object, *_args: Any, **_kwargs: Any) -> Self:
384
382
  """Responsible for creating and returning a new instance, called before __init__ when an object is instantiated."""
385
383
  return super().__new__(cls, value)
386
384
 
@@ -1295,29 +1293,68 @@ class Cmd2HelpFormatter(RichHelpFormatter):
1295
1293
 
1296
1294
  return format_tuple
1297
1295
 
1296
+ def _build_nargs_range_str(self, nargs_range: tuple[int, int | float]) -> str:
1297
+ """Generate nargs range string for help text."""
1298
+ if nargs_range[1] == constants.INFINITY:
1299
+ # {min+}
1300
+ range_str = f"{{{nargs_range[0]}+}}"
1301
+ else:
1302
+ # {min..max}
1303
+ range_str = f"{{{nargs_range[0]}..{nargs_range[1]}}}"
1304
+
1305
+ return range_str
1306
+
1298
1307
  def _format_args(self, action: argparse.Action, default_metavar: str) -> str:
1299
- """Handle ranged nargs and make other output less verbose."""
1308
+ """Override to handle cmd2's custom nargs formatting.
1309
+
1310
+ All formats in this function need to be handled by _rich_metavar_parts().
1311
+ """
1300
1312
  metavar = self._determine_metavar(action, default_metavar)
1301
1313
  metavar_formatter = self._metavar_formatter(action, default_metavar)
1302
1314
 
1303
1315
  # Handle nargs specified as a range
1304
1316
  nargs_range = action.get_nargs_range() # type: ignore[attr-defined]
1305
1317
  if nargs_range is not None:
1306
- range_str = f'{nargs_range[0]}+' if nargs_range[1] == constants.INFINITY else f'{nargs_range[0]}..{nargs_range[1]}'
1307
-
1308
- return '{}{{{}}}'.format('%s' % metavar_formatter(1), range_str) # noqa: UP031
1309
-
1310
- # Make this output less verbose. Do not customize the output when metavar is a
1311
- # tuple of strings. Allow argparse's formatter to handle that instead.
1312
- if isinstance(metavar, str):
1313
- if action.nargs == ZERO_OR_MORE:
1314
- return '[%s [...]]' % metavar_formatter(1) # noqa: UP031
1315
- if action.nargs == ONE_OR_MORE:
1316
- return '%s [...]' % metavar_formatter(1) # noqa: UP031
1317
- if isinstance(action.nargs, int) and action.nargs > 1:
1318
- return '{}{{{}}}'.format('%s' % metavar_formatter(1), action.nargs) # noqa: UP031
1318
+ arg_str = '%s' % metavar_formatter(1) # noqa: UP031
1319
+ range_str = self._build_nargs_range_str(nargs_range)
1320
+ return f"{arg_str}{range_str}"
1321
+
1322
+ # When nargs is just a number, argparse repeats the arg in the help text.
1323
+ # For instance, when nargs=5 the help text looks like: 'command arg arg arg arg arg'.
1324
+ # To make this less verbose, format it like: 'command arg{5}'.
1325
+ # Do not customize the output when metavar is a tuple of strings. Allow argparse's
1326
+ # formatter to handle that instead.
1327
+ if isinstance(metavar, str) and isinstance(action.nargs, int) and action.nargs > 1:
1328
+ arg_str = '%s' % metavar_formatter(1) # noqa: UP031
1329
+ return f"{arg_str}{{{action.nargs}}}"
1330
+
1331
+ # Fallback to parent for all other cases
1332
+ return super()._format_args(action, default_metavar)
1333
+
1334
+ def _rich_metavar_parts(
1335
+ self,
1336
+ action: argparse.Action,
1337
+ default_metavar: str,
1338
+ ) -> Iterator[tuple[str, bool]]:
1339
+ """Override to handle all cmd2-specific formatting in _format_args()."""
1340
+ metavar = self._determine_metavar(action, default_metavar)
1341
+ metavar_formatter = self._metavar_formatter(action, default_metavar)
1319
1342
 
1320
- return super()._format_args(action, default_metavar) # type: ignore[arg-type]
1343
+ # Handle nargs specified as a range
1344
+ nargs_range = action.get_nargs_range() # type: ignore[attr-defined]
1345
+ if nargs_range is not None:
1346
+ yield "%s" % metavar_formatter(1), True # noqa: UP031
1347
+ yield self._build_nargs_range_str(nargs_range), False
1348
+ return
1349
+
1350
+ # Handle specific integer nargs (e.g., nargs=5 -> arg{5})
1351
+ if isinstance(metavar, str) and isinstance(action.nargs, int) and action.nargs > 1:
1352
+ yield "%s" % metavar_formatter(1), True # noqa: UP031
1353
+ yield f"{{{action.nargs}}}", False
1354
+ return
1355
+
1356
+ # Fallback to parent for all other cases
1357
+ yield from super()._rich_metavar_parts(action, default_metavar)
1321
1358
 
1322
1359
 
1323
1360
  class RawDescriptionCmd2HelpFormatter(
cmd2/parsing.py CHANGED
@@ -9,6 +9,8 @@ from dataclasses import (
9
9
  )
10
10
  from typing import Any
11
11
 
12
+ from typing_extensions import Self
13
+
12
14
  from . import (
13
15
  constants,
14
16
  utils,
@@ -144,7 +146,7 @@ class Statement(str): # noqa: SLOT000
144
146
  # Used in JSON dictionaries
145
147
  _args_field = 'args'
146
148
 
147
- def __new__(cls, value: object, *_pos_args: Any, **_kw_args: Any) -> 'Statement':
149
+ def __new__(cls, value: object, *_pos_args: Any, **_kw_args: Any) -> Self:
148
150
  """Create a new instance of Statement.
149
151
 
150
152
  We must override __new__ because we are subclassing `str` which is
cmd2/rich_utils.py CHANGED
@@ -1,10 +1,7 @@
1
1
  """Provides common utilities to support Rich in cmd2-based applications."""
2
2
 
3
3
  import re
4
- from collections.abc import (
5
- Iterable,
6
- Mapping,
7
- )
4
+ from collections.abc import Mapping
8
5
  from enum import Enum
9
6
  from typing import (
10
7
  IO,
@@ -22,7 +19,6 @@ from rich.console import (
22
19
  from rich.padding import Padding
23
20
  from rich.pretty import is_expandable
24
21
  from rich.protocol import rich_cast
25
- from rich.segment import Segment
26
22
  from rich.style import StyleType
27
23
  from rich.table import (
28
24
  Column,
@@ -380,72 +376,3 @@ def _from_ansi_has_newline_bug() -> bool:
380
376
  # Only apply the monkey patch if the bug is present
381
377
  if _from_ansi_has_newline_bug():
382
378
  Text.from_ansi = _from_ansi_wrapper # type: ignore[assignment]
383
-
384
-
385
- ###################################################################################
386
- # Segment.apply_style() monkey patch
387
- ###################################################################################
388
-
389
- # Save original Segment.apply_style() so we can call it in our wrapper
390
- _orig_segment_apply_style = Segment.apply_style
391
-
392
-
393
- @classmethod # type: ignore[misc]
394
- def _apply_style_wrapper(cls: type[Segment], *args: Any, **kwargs: Any) -> Iterable["Segment"]:
395
- r"""Wrap Segment.apply_style() to fix bug with styling newlines.
396
-
397
- This wrapper handles an issue where Segment.apply_style() includes newlines
398
- within styled Segments. As a result, when printing text using a background color
399
- and soft wrapping, the background color incorrectly carries over onto the following line.
400
-
401
- You can reproduce this behavior by calling console.print() using a background color
402
- and soft wrapping.
403
-
404
- For example:
405
- console.print("line_1", style="blue on white", soft_wrap=True)
406
-
407
- When soft wrapping is disabled, console.print() splits Segments into their individual
408
- lines, which separates the newlines from the styled text. Therefore, the background color
409
- issue does not occur in that mode.
410
-
411
- This function copies that behavior to fix this the issue even when soft wrapping is enabled.
412
-
413
- There is currently a pull request on Rich to fix this.
414
- https://github.com/Textualize/rich/pull/3839
415
- """
416
- styled_segments = list(_orig_segment_apply_style(*args, **kwargs))
417
- newline_segment = cls.line()
418
-
419
- # If the final segment ends in a newline, that newline will be stripped by Segment.split_lines().
420
- # Save an unstyled newline to restore later.
421
- end_segment = newline_segment if styled_segments and styled_segments[-1].text.endswith("\n") else None
422
-
423
- # Use Segment.split_lines() to separate the styled text from the newlines.
424
- # This way the ANSI reset code will appear before any newline.
425
- sanitized_segments: list[Segment] = []
426
-
427
- lines = list(Segment.split_lines(styled_segments))
428
- for index, line in enumerate(lines):
429
- sanitized_segments.extend(line)
430
- if index < len(lines) - 1:
431
- sanitized_segments.append(newline_segment)
432
-
433
- if end_segment is not None:
434
- sanitized_segments.append(end_segment)
435
-
436
- return sanitized_segments
437
-
438
-
439
- def _rich_has_styled_newline_bug() -> bool:
440
- """Check if newlines are styled when soft wrapping."""
441
- console = Console(force_terminal=True)
442
- with console.capture() as capture:
443
- console.print("line_1", style="blue on white", soft_wrap=True)
444
-
445
- # Check if we see a styled newline in the output
446
- return "\x1b[34;47m\n\x1b[0m" in capture.get()
447
-
448
-
449
- # Only apply the monkey patch if the bug is present
450
- if _rich_has_styled_newline_bug():
451
- Segment.apply_style = _apply_style_wrapper # type: ignore[assignment]
cmd2/styles.py CHANGED
@@ -51,7 +51,6 @@ class Cmd2Style(StrEnum):
51
51
 
52
52
  COMMAND_LINE = "cmd2.example" # Command line examples in help text
53
53
  ERROR = "cmd2.error" # Error text (used by perror())
54
- EXCEPTION_TYPE = "cmd2.exception.type" # Used by pexcept to mark an exception type
55
54
  HELP_HEADER = "cmd2.help.header" # Help table header text
56
55
  HELP_LEADER = "cmd2.help.leader" # Text right before the help tables are listed
57
56
  SUCCESS = "cmd2.success" # Success text (used by psuccess())
@@ -63,9 +62,8 @@ class Cmd2Style(StrEnum):
63
62
  DEFAULT_CMD2_STYLES: dict[str, StyleType] = {
64
63
  Cmd2Style.COMMAND_LINE: Style(color=Color.CYAN, bold=True),
65
64
  Cmd2Style.ERROR: Style(color=Color.BRIGHT_RED),
66
- Cmd2Style.EXCEPTION_TYPE: Style(color=Color.DARK_ORANGE, bold=True),
67
- Cmd2Style.HELP_HEADER: Style(color=Color.BRIGHT_GREEN, bold=True),
68
- Cmd2Style.HELP_LEADER: Style(color=Color.CYAN, bold=True),
65
+ Cmd2Style.HELP_HEADER: Style(color=Color.BRIGHT_GREEN),
66
+ Cmd2Style.HELP_LEADER: Style(color=Color.CYAN),
69
67
  Cmd2Style.SUCCESS: Style(color=Color.GREEN),
70
68
  Cmd2Style.TABLE_BORDER: Style(color=Color.BRIGHT_GREEN),
71
69
  Cmd2Style.WARNING: Style(color=Color.BRIGHT_YELLOW),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cmd2
3
- Version: 3.1.0
3
+ Version: 3.1.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-Expression: MIT
@@ -26,14 +26,14 @@ Requires-Dist: backports.strenum; python_version == "3.10"
26
26
  Requires-Dist: gnureadline>=8; platform_system == "Darwin"
27
27
  Requires-Dist: pyperclip>=1.8.2
28
28
  Requires-Dist: pyreadline3>=3.4; platform_system == "Windows"
29
- Requires-Dist: rich>=14.1.0
29
+ Requires-Dist: rich>=14.3.0
30
30
  Requires-Dist: rich-argparse>=1.7.1
31
31
  Dynamic: license-file
32
32
 
33
33
  <h1 align="center">cmd2 : immersive interactive command line applications</h1>
34
34
 
35
35
  [![Latest Version](https://img.shields.io/pypi/v/cmd2.svg?style=flat-square&label=latest%20stable%20version)](https://pypi.python.org/pypi/cmd2/)
36
- [![GitHub Actions](https://github.com/python-cmd2/cmd2/workflows/CI/badge.svg)](https://github.com/python-cmd2/cmd2/actions?query=workflow%3ACI)
36
+ [![Tests](https://github.com/python-cmd2/cmd2/actions/workflows/tests.yml/badge.svg)](https://github.com/python-cmd2/cmd2/actions/workflows/tests.yml)
37
37
  [![codecov](https://codecov.io/gh/python-cmd2/cmd2/branch/main/graph/badge.svg)](https://codecov.io/gh/python-cmd2/cmd2)
38
38
  [![Documentation Status](https://readthedocs.org/projects/cmd2/badge/?version=latest)](http://cmd2.readthedocs.io/en/latest/?badge=latest)
39
39
  <a href="https://discord.gg/RpVG6tk"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg" alt="Chat"></a>
@@ -1,6 +1,6 @@
1
1
  cmd2/__init__.py,sha256=JG-jiy2MMArRTujSiU8usvQpdgQbl3KLTim4tU5SCJw,2278
2
2
  cmd2/argparse_completer.py,sha256=8hK5_QUnHgeHVY60C89kMKn2b91AUyDjyq4dykzjCRA,35680
3
- cmd2/argparse_custom.py,sha256=m5t3zfOe5BNALOmHW42Xdf3zrWYaoV-CJJGaDjJXdDk,67243
3
+ cmd2/argparse_custom.py,sha256=7h68TXurxjDoMsCzoZy1uyy6tvTEEi3RornPQ9ZHJoA,68767
4
4
  cmd2/clipboard.py,sha256=5PSKTe3uDe2pFFEDUEMMwAmzcyPkbXXz14SOQxFFncg,515
5
5
  cmd2/cmd2.py,sha256=rHSkJ2RF9QQiYdIktd5DHJYJL-X7swwzyVqM6TMbSZU,272373
6
6
  cmd2/colors.py,sha256=gLAU8gjhPwZud9MNVk53RGE9ZSdwspLnye_kReeZKjc,7848
@@ -9,19 +9,19 @@ cmd2/constants.py,sha256=yDcaeEG4Y2DHmLwUpV-_lEiHkiYUZEuyf9mpDdAzmkg,1773
9
9
  cmd2/decorators.py,sha256=_xxagAxlcUROLZowqjW5RYP935uNMEJyr2h8FkWalGw,17356
10
10
  cmd2/exceptions.py,sha256=J8Ck0dhusB2cfVksRLkM4WVgBTGQnPdEcU26RfBjjRw,3440
11
11
  cmd2/history.py,sha256=QNg5QOe3854hFsu9qw7QlqtnTQvmtXojOWgMZlg6JoQ,14755
12
- cmd2/parsing.py,sha256=d97-cnnlFi5HLVuoqjMCOhEc3l8LO8jCbwnwXkOgxro,28006
12
+ cmd2/parsing.py,sha256=-8TfpXZAZnlLbCJem_EziaC2JqZvmOkIUoera1xmM4Y,28035
13
13
  cmd2/plugin.py,sha256=_KD44QaHPXncT3qSOWXyzesxKvEbJp5tQeMJ569mLNw,827
14
14
  cmd2/py.typed,sha256=qrkHrYJvGoZpU2BpVLNxJB44LlhqVSKyYOwD_L_1m3s,10
15
15
  cmd2/py_bridge.py,sha256=oD7EwT59HvWSvF9FACWDi-_izkQY6mfitOYX20Y5daI,5190
16
- cmd2/rich_utils.py,sha256=hYD5Cpl1fAYeau3l8NuJIO9SGiC44Ew9vTKHret6QMo,16364
16
+ cmd2/rich_utils.py,sha256=-BZW98c5OEdYpPq9J3LDkgWHPGoAb8CGMbZvFQrsONE,13461
17
17
  cmd2/rl_utils.py,sha256=tlDsH5QnN5xlkJT62Q4ZSOHvccXtt1Nm6kcWJQh6cUc,11315
18
18
  cmd2/string_utils.py,sha256=7F8ORonOc2xS0ZC56hjhQcnZhonE3lDFdQxij6v54dA,4430
19
- cmd2/styles.py,sha256=kuekxVEr0kOrDNJJbVFqOl96YqX8M6lheKBCBS1nGSU,2945
19
+ cmd2/styles.py,sha256=rh7N-4A2moKDEH82Co-yTrmzAknU75d2c0oR2G1euEg,2762
20
20
  cmd2/terminal_utils.py,sha256=AOk1VjOAzxn7jou2wbALeD6FRIRlKgkQ0VCzIEY51DA,6052
21
21
  cmd2/transcript.py,sha256=5a_1HGDzhhG1I47g9QnrVNWnOI6Mt_z7jGMH7wFm2XQ,9210
22
22
  cmd2/utils.py,sha256=4jlhcHfgr8yoymP2hRPeCr7_1J37vI4MIIBkszyPK6M,32292
23
- cmd2-3.1.0.dist-info/licenses/LICENSE,sha256=9qPeHY4u2fkSz0JQGT-P4T3QqTWTqnQJ_8LkZUhSdFY,1099
24
- cmd2-3.1.0.dist-info/METADATA,sha256=_YyjT-gVHX70eWwdFhXpgZ8IenPTPZE2ue14SS6yBvo,16340
25
- cmd2-3.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- cmd2-3.1.0.dist-info/top_level.txt,sha256=gJbOJmyrARwLhm5diXAtzlNQdxbDZ8iRJ8HJi65_5hg,5
27
- cmd2-3.1.0.dist-info/RECORD,,
23
+ cmd2-3.1.1.dist-info/licenses/LICENSE,sha256=7JL3v4rPUpHV8b0yWAOl64e9r4YmwbHgl3SH_RGbsK8,1099
24
+ cmd2-3.1.1.dist-info/METADATA,sha256=KL2uOqWMRRCYFEWwYiSbejEAtzY0IqYTIZR7mSwgz0M,16346
25
+ cmd2-3.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
26
+ cmd2-3.1.1.dist-info/top_level.txt,sha256=gJbOJmyrARwLhm5diXAtzlNQdxbDZ8iRJ8HJi65_5hg,5
27
+ cmd2-3.1.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,18 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2008-2026 Catherine Devlin and others
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
6
+ associated documentation files (the "Software"), to deal in the Software without restriction,
7
+ including without limitation the rights to use, copy, modify, merge, publish, distribute,
8
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all copies or substantial
12
+ portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
15
+ NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
16
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
17
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2008-2025 Catherine Devlin and others
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.