Skip to content

Commit 0b9f96c

Browse files
committed
Remove support for Python 3.7.
Add unit testing for Python 3.12. Remove deprecated features.
1 parent be15939 commit 0b9f96c

File tree

21 files changed

+58
-116
lines changed

21 files changed

+58
-116
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
## 2.5.0 (TBD)
22
* Breaking Change
3-
* `cmd2` 2.5 supports Python 3.7+ (removed support for Python 3.6)
3+
* `cmd2` 2.5 supports Python 3.8+ (removed support for Python 3.6 and 3.7)
44
* Enhancements
55
* Removed dependency on `attrs` and replaced with [dataclasses](https://docs.python.org/3/library/dataclasses.html)
66
* add `allow_clipboard` initialization parameter and attribute to disable ability to
77
add output to the operating system clipboard
8+
* Updated unit tests to be Python 3.12 compliant.
9+
* Deletions (potentially breaking changes)
10+
* Removed `apply_style` from `Cmd.pwarning()`.
811

912

1013
## 2.4.3 (January 27, 2023)

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ The developers toolbox
3232
![system schema](https://raw.githubusercontent.com/python-cmd2/cmd2/master/.github/images/graph.drawio.png)
3333

3434

35-
When creating solutions developers have no shortage of tools to create rich and smart user interfaces.
36-
System administrators have long been duct taping together brittle workflows based on a menagerie of simple command line tools created by strangers on github and the guy down the hall.
37-
Unfortunately, when CLIs become significantly complex the ease of command discoverability tends to fade quickly.
38-
On the other hand, Web and traditional desktop GUIs are first in class when it comes to easily discovering functionality.
35+
When creating solutions developers have no shortage of tools to create rich and smart user interfaces.
36+
System administrators have long been duct taping together brittle workflows based on a menagerie of simple command line tools created by strangers on github and the guy down the hall.
37+
Unfortunately, when CLIs become significantly complex the ease of command discoverability tends to fade quickly.
38+
On the other hand, Web and traditional desktop GUIs are first in class when it comes to easily discovering functionality.
3939
The price we pay for beautifully colored displays is complexity required to aggregate disperate applications into larger systems.
40-
`cmd2` fills the niche between high [ease of command discovery](https://clig.dev/#ease-of-discovery) applications and smart workflow automation systems.
40+
`cmd2` fills the niche between high [ease of command discovery](https://clig.dev/#ease-of-discovery) applications and smart workflow automation systems.
4141

42-
The `cmd2` framework provides a great mixture of both worlds. Application designers can easily create complex applications and rely on the cmd2 library to offer effortless user facing help and extensive tab completion.
42+
The `cmd2` framework provides a great mixture of both worlds. Application designers can easily create complex applications and rely on the cmd2 library to offer effortless user facing help and extensive tab completion.
4343
When users become comfortable with functionality, cmd2 turns into a feature rich library enabling a smooth transition to full automation. If designed with enough forethought, a well implemented cmd2 application can serve as a boutique workflow tool. `cmd2` pulls off this flexibility based on two pillars of philosophy:
4444

4545
* Tab Completion
@@ -78,7 +78,7 @@ On all operating systems, the latest stable version of `cmd2` can be installed u
7878
pip install -U cmd2
7979
```
8080

81-
cmd2 works with Python 3.7+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies.
81+
cmd2 works with Python 3.8+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies.
8282

8383
For information on other installation options, see
8484
[Installation Instructions](https://cmd2.readthedocs.io/en/latest/overview/installation.html) in the cmd2
@@ -97,7 +97,7 @@ The best way to learn the cmd2 api is to delve into the example applications loc
9797
Tutorials
9898
---------
9999

100-
* PyOhio 2019 presentation:
100+
* PyOhio 2019 presentation:
101101
* [video](https://www.youtube.com/watch?v=pebeWrTqIIw)
102102
* [slides](https://github.com/python-cmd2/talks/blob/master/PyOhio_2019/cmd2-PyOhio_2019.pdf)
103103
* [example code](https://github.com/python-cmd2/talks/tree/master/PyOhio_2019/examples)
@@ -161,4 +161,4 @@ Projects using cmd2
161161
Possibly defunct but still good examples
162162

163163
* [JSShell](https://github.com/Den1al/JSShell)
164-
* [FLASHMINGO](https://github.com/fireeye/flashmingo)
164+
* [FLASHMINGO](https://github.com/fireeye/flashmingo)

cmd2/argparse_custom.py

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -257,32 +257,21 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens)
257257
List,
258258
NoReturn,
259259
Optional,
260+
Protocol,
260261
Sequence,
261262
Set,
262263
Tuple,
263264
Type,
264265
Union,
265266
cast,
267+
runtime_checkable,
266268
)
267269

268270
from . import (
269271
ansi,
270272
constants,
271273
)
272274

273-
try:
274-
from typing import (
275-
Protocol,
276-
runtime_checkable,
277-
)
278-
except ImportError:
279-
# Remove these imports when we no longer support Python 3.7
280-
from typing_extensions import ( # type: ignore[assignment]
281-
Protocol,
282-
runtime_checkable,
283-
)
284-
285-
286275
if TYPE_CHECKING: # pragma: no cover
287276
from .argparse_completer import (
288277
ArgparseCompleter,
@@ -352,8 +341,7 @@ class ChoicesProviderFuncBase(Protocol):
352341
Function that returns a list of choices in support of tab completion
353342
"""
354343

355-
def __call__(self) -> List[str]:
356-
... # pragma: no cover
344+
def __call__(self) -> List[str]: ... # pragma: no cover
357345

358346

359347
@runtime_checkable
@@ -362,8 +350,7 @@ class ChoicesProviderFuncWithTokens(Protocol):
362350
Function that returns a list of choices in support of tab completion and accepts a dictionary of prior arguments.
363351
"""
364352

365-
def __call__(self, *, arg_tokens: Dict[str, List[str]] = {}) -> List[str]:
366-
... # pragma: no cover
353+
def __call__(self, *, arg_tokens: Dict[str, List[str]] = {}) -> List[str]: ... # pragma: no cover
367354

368355

369356
ChoicesProviderFunc = Union[ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens]
@@ -381,8 +368,7 @@ def __call__(
381368
line: str,
382369
begidx: int,
383370
endidx: int,
384-
) -> List[str]:
385-
... # pragma: no cover
371+
) -> List[str]: ... # pragma: no cover
386372

387373

388374
@runtime_checkable
@@ -400,8 +386,7 @@ def __call__(
400386
endidx: int,
401387
*,
402388
arg_tokens: Dict[str, List[str]] = {},
403-
) -> List[str]:
404-
... # pragma: no cover
389+
) -> List[str]: ... # pragma: no cover
405390

406391

407392
CompleterFunc = Union[CompleterFuncBase, CompleterFuncWithTokens]

cmd2/cmd2.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None:
780780

781781
methods = inspect.getmembers(
782782
cmdset,
783-
predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type]
783+
predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type, var-annotated]
784784
and hasattr(meth, '__name__')
785785
and meth.__name__.startswith(COMMAND_FUNC_PREFIX),
786786
)
@@ -811,7 +811,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None:
811811
def _check_uninstallable(self, cmdset: CommandSet) -> None:
812812
methods = inspect.getmembers(
813813
cmdset,
814-
predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type]
814+
predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type, var-annotated]
815815
and hasattr(meth, '__name__')
816816
and meth.__name__.startswith(COMMAND_FUNC_PREFIX),
817817
)
@@ -1223,24 +1223,17 @@ def pwarning(
12231223
msg: Any = '',
12241224
*,
12251225
end: str = '\n',
1226-
apply_style: bool = True,
12271226
paged: bool = False,
12281227
chop: bool = False,
12291228
) -> None:
12301229
"""Wraps perror, but applies ansi.style_warning by default
12311230
12321231
:param msg: object to print
12331232
:param end: string appended after the end of the message, default a newline
1234-
:param apply_style:
1235-
If True, then ansi.style_warning will be applied to the message text. Set to False in cases
1236-
where the message text already has the desired style. Defaults to True.
1237-
1238-
.. deprecated: 2.4.4
1239-
Use :meth:`~cmd2.Cmd.print_to` instead to print to stderr without style applied.
12401233
:param paged: If True, pass the output through the configured pager.
12411234
:param chop: If paged is True, True to truncate long lines or False to wrap long lines.
12421235
"""
1243-
self.print_to(sys.stderr, msg, end=end, style=ansi.style_warning if apply_style else None, paged=paged, chop=chop)
1236+
self.print_to(sys.stderr, msg, end=end, style=ansi.style_warning, paged=paged, chop=chop)
12441237

12451238
def pfailure(
12461239
self,

cmd2/decorators.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,7 @@ def with_argparser(
266266
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
267267
preserve_quotes: bool = False,
268268
with_unknown_args: bool = False,
269-
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]:
270-
... # pragma: no cover
269+
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]: ... # pragma: no cover
271270

272271

273272
@overload
@@ -277,8 +276,7 @@ def with_argparser(
277276
ns_provider: Optional[Callable[..., argparse.Namespace]] = None,
278277
preserve_quotes: bool = False,
279278
with_unknown_args: bool = False,
280-
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]:
281-
... # pragma: no cover
279+
) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]: ... # pragma: no cover
282280

283281

284282
def with_argparser(
@@ -418,8 +416,7 @@ def as_subcommand_to(
418416
*,
419417
help: Optional[str] = None,
420418
aliases: Optional[List[str]] = None,
421-
) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]:
422-
... # pragma: no cover
419+
) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: ... # pragma: no cover
423420

424421

425422
@overload
@@ -430,8 +427,7 @@ def as_subcommand_to(
430427
*,
431428
help: Optional[str] = None,
432429
aliases: Optional[List[str]] = None,
433-
) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]:
434-
... # pragma: no cover
430+
) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: ... # pragma: no cover
435431

436432

437433
def as_subcommand_to(

cmd2/history.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,10 @@ def _zero_based_index(self, onebased: Union[int, str]) -> int:
154154
return result
155155

156156
@overload
157-
def append(self, new: HistoryItem) -> None:
158-
... # pragma: no cover
157+
def append(self, new: HistoryItem) -> None: ... # pragma: no cover
159158

160159
@overload
161-
def append(self, new: Statement) -> None:
162-
... # pragma: no cover
160+
def append(self, new: Statement) -> None: ... # pragma: no cover
163161

164162
def append(self, new: Union[Statement, HistoryItem]) -> None:
165163
"""Append a new statement to the end of the History list.

docs/features/argument_processing.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,8 @@ Here's what it looks like::
7878
.. warning::
7979

8080
It is important that each command which uses the ``@with_argparser``
81-
decorator be passed a unique instance of a parser. This limitation is due
82-
to bugs in CPython prior to Python 3.7 which make it impossible to make a
83-
deep copy of an instance of a ``argparse.ArgumentParser``.
81+
decorator be passed a unique instance of a parser since command-specific
82+
changes could be made to it.
8483

8584

8685
.. note::

docs/overview/installation.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Installation Instructions
77
.. _setuptools: https://pypi.org/project/setuptools
88
.. _PyPI: https://pypi.org
99

10-
``cmd2`` works on Linux, macOS, and Windows. It requires Python 3.7 or
10+
``cmd2`` works on Linux, macOS, and Windows. It requires Python 3.8 or
1111
higher, pip_, and setuptools_. If you've got all that, then you can just:
1212

1313
.. code-block:: shell
@@ -30,7 +30,7 @@ higher, pip_, and setuptools_. If you've got all that, then you can just:
3030
Prerequisites
3131
-------------
3232

33-
If you have Python 3 >=3.7 installed from `python.org
33+
If you have Python 3 >=3.8 installed from `python.org
3434
<https://www.python.org>`_, you will already have pip_ and setuptools_, but may
3535
need to upgrade to the latest versions:
3636

noxfile.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import nox
22

33

4-
@nox.session(python=['3.11'])
4+
@nox.session(python=['3.12'])
55
def docs(session):
66
session.install(
77
'sphinx',
@@ -17,7 +17,7 @@ def docs(session):
1717
)
1818

1919

20-
@nox.session(python=['3.7', '3.8', '3.9', '3.10', '3.11'])
20+
@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12'])
2121
@nox.parametrize('plugin', [None, 'ext_test', 'template', 'coverage'])
2222
def tests(session, plugin):
2323
if plugin is None:
@@ -41,7 +41,7 @@ def tests(session, plugin):
4141
)
4242

4343

44-
@nox.session(python=['3.8', '3.9', '3.10', '3.11'])
44+
@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12'])
4545
@nox.parametrize('step', ['mypy', 'flake8'])
4646
def validate(session, step):
4747
session.install('invoke', './[validate]')

plugins/ext_test/build-pyenvs.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# version numbers are: major.minor.patch
99
#
1010
# this script will delete and recreate existing virtualenvs named
11-
# cmd2-3.7, etc. It will also create a .python-version
11+
# cmd2-3.8, etc. It will also create a .python-version
1212
#
1313
# Prerequisites:
1414
# - *nix-ish environment like macOS or Linux
@@ -23,7 +23,7 @@
2323
# virtualenvs will be added to '.python-version'. Feel free to modify
2424
# this list, but note that this script intentionally won't install
2525
# dev, rc, or beta python releases
26-
declare -a pythons=("3.7" "3.8" "3.9", "3.10", "3.11")
26+
declare -a pythons=("3.8" "3.9", "3.10", "3.11", "3.12")
2727

2828
# function to find the latest patch of a minor version of python
2929
function find_latest_version {

0 commit comments

Comments
 (0)