Skip to content

Commit a61513e

Browse files
authored
Add support for Python 3.14 and remove support for Python 3.8 (#1419)
* Add support for Python 3.14 and remove support for Python 3.8 Also: - Add ruff linting to "make check" - Upgrade version of ruff used by "make check" - Upgrade version of prettier used by "make check" * Try to fix unit tests for Python 3.14 * Fix tests failing on Windows for Python 3.14 * Improved a few tests to be slightly stricter, but permissive enough they work on Windows with Python 3.14 * Enable some additional ruff lint check rules and enable auto-fix of lint rules on "make check"
1 parent 4788e2d commit a61513e

File tree

17 files changed

+307
-184
lines changed

17 files changed

+307
-184
lines changed

.github/CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ The tables below list all prerequisites along with the minimum required version
4848

4949
| Prerequisite | Minimum Version |
5050
| --------------------------------------------------- | --------------- |
51-
| [python](https://www.python.org/downloads/) | `3.8` |
51+
| [python](https://www.python.org/downloads/) | `3.9` |
5252
| [pyperclip](https://github.com/asweigart/pyperclip) | `1.8.2` |
5353
| [wcwidth](https://pypi.python.org/pypi/wcwidth) | `0.2.12` |
5454

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
os: [ubuntu-latest, macos-latest, windows-latest]
13-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
13+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14-dev"]
1414
runs-on: ${{ matrix.os }}
1515
steps:
1616
- uses: actions/checkout@v4 # https://github.com/actions/checkout

.pre-commit-config.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ repos:
88
- id: trailing-whitespace
99

1010
- repo: https://github.com/astral-sh/ruff-pre-commit
11-
rev: "v0.9.3"
11+
rev: "v0.11.10"
1212
hooks:
1313
- id: ruff-format
1414
args: [--config=pyproject.toml]
15+
- id: ruff-check
16+
args: [--config=pyproject.toml, --fix]
1517

1618
- repo: https://github.com/pre-commit/mirrors-prettier
1719
rev: "v3.1.0"
1820
hooks:
1921
- id: prettier
2022
additional_dependencies:
21-
- prettier@3.4.2
22-
23+
- prettier@3.5.3
24+

CHANGELOG.md

Lines changed: 117 additions & 51 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,21 @@ when using cmd.
3030
![system schema](https://raw.githubusercontent.com/python-cmd2/cmd2/master/.github/images/graph.drawio.png)
3131

3232
When creating solutions developers have no shortage of tools to create rich and smart user interfaces.
33-
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.
33+
System administrators have long been duct taping together brittle workflows based on a menagerie of simple command line
34+
tools created by strangers on github and the guy down the hall.
3435
Unfortunately, when CLIs become significantly complex the ease of command discoverability tends to fade quickly.
35-
On the other hand, Web and traditional desktop GUIs are first in class when it comes to easily discovering functionality.
36-
The price we pay for beautifully colored displays is complexity required to aggregate disperate applications into larger systems.
37-
`cmd2` fills the niche between high [ease of command discovery](https://clig.dev/#ease-of-discovery) applications and smart workflow automation systems.
38-
39-
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.
40-
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:
36+
On the other hand, Web and traditional desktop GUIs are first in class when it comes to easily discovering
37+
functionality.
38+
The price we pay for beautifully colored displays is complexity required to aggregate disperate applications into larger
39+
systems.
40+
`cmd2` fills the niche between high [ease of command discovery](https://clig.dev/#ease-of-discovery) applications and
41+
smart workflow automation systems.
42+
43+
The `cmd2` framework provides a great mixture of both worlds. Application designers can easily create complex
44+
applications and rely on the cmd2 library to offer effortless user facing help and extensive tab completion.
45+
When users become comfortable with functionality, cmd2 turns into a feature rich library enabling a smooth transition to
46+
full automation. If designed with enough forethought, a well implemented cmd2 application can serve as a boutique
47+
workflow tool. `cmd2` pulls off this flexibility based on two pillars of philosophy:
4148

4249
- Tab Completion
4350
- Automation Transition
@@ -46,7 +53,8 @@ When users become comfortable with functionality, cmd2 turns into a feature rich
4653

4754
<a href="https://imgflip.com/i/63h03x"><img src="https://i.imgflip.com/63h03x.jpg" title="made at imgflip.com" width="70%" height="%70"/></a>
4855

49-
Deep extensive tab completion and help text generation based on the argparse library create the first pillar of 'ease of command discovery'. The following is a list of features in this category.
56+
Deep extensive tab completion and help text generation based on the argparse library create the first pillar of 'ease of
57+
command discovery'. The following is a list of features in this category.
5058

5159
- Great tab completion of commands, subcommands, file system paths, and shell commands.
5260
- Custom tab completion for user designed commands via simple function overloading.
@@ -57,7 +65,8 @@ Deep extensive tab completion and help text generation based on the argparse lib
5765

5866
<a href="https://imgflip.com/i/66t0y0"><img src="https://i.imgflip.com/66t0y0.jpg" title="made at imgflip.com" width="70%" height="70%"/></a>
5967

60-
cmd2 creates the second pillar of 'ease of transition to automation' through alias/macro creation, command line argument parsing and execution of cmd2 scripting.
68+
cmd2 creates the second pillar of 'ease of transition to automation' through alias/macro creation, command line argument
69+
parsing and execution of cmd2 scripting.
6170

6271
- Flexible alias and macro creation for quick abstraction of commands.
6372
- Text file scripting of your application with `run_script` (`@`) and `_relative_run_script` (`@@`)
@@ -72,7 +81,7 @@ On all operating systems, the latest stable version of `cmd2` can be installed u
7281
pip install -U cmd2
7382
```
7483

75-
cmd2 works with Python 3.8+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies.
84+
cmd2 works with Python 3.9+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies.
7685

7786
For information on other installation options, see
7887
[Installation Instructions](https://cmd2.readthedocs.io/en/latest/overview/installation.html) in the cmd2
@@ -94,7 +103,8 @@ The best way to learn the cmd2 api is to delve into the example applications loc
94103
- [example code](https://github.com/python-cmd2/talks/tree/master/PyOhio_2019/examples)
95104
- [Cookiecutter](https://github.com/cookiecutter/cookiecutter) Templates from community
96105
- Basic cookiecutter template for cmd2 application : https://github.com/jayrod/cookiecutter-python-cmd2
97-
- Advanced cookiecutter template with external plugin support : https://github.com/jayrod/cookiecutter-python-cmd2-ext-plug
106+
- Advanced cookiecutter template with external plugin
107+
support : https://github.com/jayrod/cookiecutter-python-cmd2-ext-plug
98108
- [cmd2 example applications](https://github.com/python-cmd2/cmd2/tree/master/examples)
99109
- Basic cmd2 examples to demonstrate how to use various features
100110
- [Advanced Examples](https://github.com/jayrod/cmd2-example-apps)
@@ -111,19 +121,25 @@ import cmd2
111121
class FirstApp(cmd2.Cmd):
112122
"""A simple cmd2 application."""
113123

114-
def do_hello_world(self, _: cmd2.Statement):
124+
125+
def do_hello_world(self, _: cmd2.Statement):
115126
self.poutput('Hello World')
116127

128+
117129
if __name__ == '__main__':
118130
import sys
131+
119132
c = FirstApp()
120133
sys.exit(c.cmdloop())
121134

122135
```
123136

124137
## Found a bug?
125138

126-
If you think you've found a bug, please first read through the open [Issues](https://github.com/python-cmd2/cmd2/issues). If you're confident it's a new bug, go ahead and create a new GitHub issue. Be sure to include as much information as possible so we can reproduce the bug. At a minimum, please state the following:
139+
If you think you've found a bug, please first read through the
140+
open [Issues](https://github.com/python-cmd2/cmd2/issues). If you're confident it's a new bug, go ahead and create a new
141+
GitHub issue. Be sure to include as much information as possible so we can reproduce the bug. At a minimum, please state
142+
the following:
127143

128144
- `cmd2` version
129145
- Python version

cmd2/argparse_custom.py

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,9 @@ def __init__(
12521252
conflict_handler: str = 'error',
12531253
add_help: bool = True,
12541254
allow_abbrev: bool = True,
1255+
exit_on_error: bool = True,
1256+
suggest_on_error: bool = False,
1257+
color: bool = False,
12551258
*,
12561259
ap_completer_type: Optional[Type['ArgparseCompleter']] = None,
12571260
) -> None:
@@ -1262,20 +1265,42 @@ def __init__(
12621265
behavior on this parser. If this is None or not present, then cmd2 will use
12631266
argparse_completer.DEFAULT_AP_COMPLETER when tab completing this parser's arguments
12641267
"""
1265-
super(Cmd2ArgumentParser, self).__init__(
1266-
prog=prog,
1267-
usage=usage,
1268-
description=description,
1269-
epilog=epilog,
1270-
parents=parents if parents else [],
1271-
formatter_class=formatter_class, # type: ignore[arg-type]
1272-
prefix_chars=prefix_chars,
1273-
fromfile_prefix_chars=fromfile_prefix_chars,
1274-
argument_default=argument_default,
1275-
conflict_handler=conflict_handler,
1276-
add_help=add_help,
1277-
allow_abbrev=allow_abbrev,
1278-
)
1268+
if sys.version_info[1] >= 14:
1269+
# Python >= 3.14 so pass new arguments to parent argparse.ArgumentParser class
1270+
super(Cmd2ArgumentParser, self).__init__(
1271+
prog=prog,
1272+
usage=usage,
1273+
description=description,
1274+
epilog=epilog,
1275+
parents=parents if parents else [],
1276+
formatter_class=formatter_class, # type: ignore[arg-type]
1277+
prefix_chars=prefix_chars,
1278+
fromfile_prefix_chars=fromfile_prefix_chars,
1279+
argument_default=argument_default,
1280+
conflict_handler=conflict_handler,
1281+
add_help=add_help,
1282+
allow_abbrev=allow_abbrev,
1283+
exit_on_error=exit_on_error,
1284+
suggest_on_error=suggest_on_error,
1285+
color=color,
1286+
)
1287+
else:
1288+
# Python < 3.14, so don't pass new arguments to parent argparse.ArgumentParser class
1289+
super(Cmd2ArgumentParser, self).__init__(
1290+
prog=prog,
1291+
usage=usage,
1292+
description=description,
1293+
epilog=epilog,
1294+
parents=parents if parents else [],
1295+
formatter_class=formatter_class, # type: ignore[arg-type]
1296+
prefix_chars=prefix_chars,
1297+
fromfile_prefix_chars=fromfile_prefix_chars,
1298+
argument_default=argument_default,
1299+
conflict_handler=conflict_handler,
1300+
add_help=add_help,
1301+
allow_abbrev=allow_abbrev,
1302+
exit_on_error=exit_on_error,
1303+
)
12791304

12801305
self.set_ap_completer_type(ap_completer_type) # type: ignore[attr-defined]
12811306

docs/overview/installation.md

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Installation Instructions
22

3-
`cmd2` works on Linux, macOS, and Windows. It requires Python 3.8 or higher, [pip](https://pypi.org/project/pip), and [setuptools](https://pypi.org/project/setuptools). If you've got all that, then you can just:
3+
`cmd2` works on Linux, macOS, and Windows. It requires Python 3.9 or higher, [pip](https://pypi.org/project/pip),
4+
and [setuptools](https://pypi.org/project/setuptools). If you've got all that, then you can just:
45

56
```shell
67
$ pip install cmd2
@@ -16,7 +17,9 @@ $ pip install cmd2
1617

1718
## Prerequisites
1819

19-
If you have Python 3 >=3.8 installed from [python.org](https://www.python.org), you will already have [pip](https://pypi.org/project/pip) and [setuptools](https://pypi.org/project/setuptools), but may need to upgrade to the latest versions:
20+
If you have Python 3 >=3.9 installed from [python.org](https://www.python.org), you will already
21+
have [pip](https://pypi.org/project/pip) and [setuptools](https://pypi.org/project/setuptools), but may need to upgrade
22+
to the latest versions:
2023

2124
On Linux or OS X:
2225

@@ -32,7 +35,8 @@ C:\> python -m pip install -U pip setuptools
3235

3336
## Install from PyPI {: #pip_install }
3437

35-
[pip](https://pypi.org/project/pip) is the recommended installer. Installing packages from [PyPI](https://pypi.org) with pip is easy:
38+
[pip](https://pypi.org/project/pip) is the recommended installer. Installing packages from [PyPI](https://pypi.org) with
39+
pip is easy:
3640

3741
```shell
3842
$ pip install cmd2
@@ -42,15 +46,17 @@ This will install the required 3rd-party dependencies, if necessary.
4246

4347
## Install from GitHub {: #github }
4448

45-
The latest version of `cmd2` can be installed directly from the master branch on GitHub using [pip](https://pypi.org/project/pip):
49+
The latest version of `cmd2` can be installed directly from the master branch on GitHub
50+
using [pip](https://pypi.org/project/pip):
4651

4752
```shell
4853
$ pip install -U git+git://github.com/python-cmd2/cmd2.git
4954
```
5055

5156
## Install from Debian or Ubuntu repos
5257

53-
We recommend installing from [pip](https://pypi.org/project/pip), but if you wish to install from Debian or Ubuntu repos this can be done with apt-get.
58+
We recommend installing from [pip](https://pypi.org/project/pip), but if you wish to install from Debian or Ubuntu repos
59+
this can be done with apt-get.
5460

5561
For Python 3:
5662

@@ -78,13 +84,19 @@ If you wish to permanently uninstall `cmd2`, this can also easily be done with [
7884

7985
## readline Considerations
8086

81-
Tab completion for `cmd2` applications is only tested against GNU Readline. It does not work properly with the [libedit](http://thrysoee.dk/editline/) library which is similar, but not identical to GNU Readline. `cmd2` will disable all tab-completion support if an incompatible version of `readline` is found.
87+
Tab completion for `cmd2` applications is only tested against GNU Readline. It does not work properly with
88+
the [libedit](http://thrysoee.dk/editline/) library which is similar, but not identical to GNU Readline. `cmd2` will
89+
disable all tab-completion support if an incompatible version of `readline` is found.
8290

83-
When installed using `pip`, `uv`, or similar Python packaging tool on either `macOS` or `Windows`, `cmd2` will automatically install a compatiable version of readline.
91+
When installed using `pip`, `uv`, or similar Python packaging tool on either `macOS` or `Windows`, `cmd2` will
92+
automatically install a compatiable version of readline.
8493

85-
Most `Linux` OSes come with a compatible version of readline. However, if you are using a tool like `uv` to install Python on your system and configure a virtual environment, `uv` installed versions of Python come with `libEdit`.
94+
Most `Linux` OSes come with a compatible version of readline. However, if you are using a tool like `uv` to install
95+
Python on your system and configure a virtual environment, `uv` installed versions of Python come with `libEdit`.
8696

87-
macOS comes with the [libedit](http://thrysoee.dk/editline/) library which is similar, but not identical, to GNU Readline. Tab completion for `cmd2` applications is only tested against GNU Readline. In this case you just need to install the `gnureadline` Python package which is statically linked against GNU Readline:
97+
macOS comes with the [libedit](http://thrysoee.dk/editline/) library which is similar, but not identical, to GNU
98+
Readline. Tab completion for `cmd2` applications is only tested against GNU Readline. In this case you just need to
99+
install the `gnureadline` Python package which is statically linked against GNU Readline:
88100

89101
```shell
90102
$ pip install -U gnureadline

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.8, etc. It will also create a .python-version
11+
# cmd2-3.9, 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.8" "3.9", "3.10", "3.11", "3.12")
26+
declare -a pythons=("3.9", "3.10", "3.11", "3.12", "3.13")
2727

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

plugins/ext_test/noxfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import nox
22

33

4-
@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12'])
4+
@nox.session(python=['3.9', '3.10', '3.11', '3.12', '3.13'])
55
def tests(session):
66
session.install('invoke', './[test]')
77
session.run('invoke', 'pytest', '--junit', '--no-pty')

plugins/ext_test/setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
license='MIT',
3434
package_data=PACKAGE_DATA,
3535
packages=['cmd2_ext_test'],
36-
python_requires='>=3.8',
36+
python_requires='>=3.9',
3737
install_requires=['cmd2 >= 2, <3'],
3838
setup_requires=['setuptools >= 42', 'setuptools_scm >= 3.4'],
3939
classifiers=[
@@ -43,11 +43,12 @@
4343
'Topic :: Software Development :: Libraries :: Python Modules',
4444
'Intended Audience :: Developers',
4545
'License :: OSI Approved :: MIT License',
46-
'Programming Language :: Python :: 3.8',
4746
'Programming Language :: Python :: 3.9',
4847
'Programming Language :: Python :: 3.10',
4948
'Programming Language :: Python :: 3.11',
5049
'Programming Language :: Python :: 3.12',
50+
'Programming Language :: Python :: 3.13',
51+
'Programming Language :: Python :: 3.14',
5152
],
5253
# dependencies for development and testing
5354
# $ pip install -e .[dev]

plugins/template/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ and an example app which uses the plugin:
5151
import cmd2
5252
import cmd2_myplugin
5353

54+
5455
class Example(cmd2_myplugin.MyPlugin, cmd2.Cmd):
5556
"""An class to show how to use a plugin"""
57+
5658
def __init__(self, *args, **kwargs):
5759
# code placed here runs before cmd2.Cmd or
5860
# any plugins initialize
@@ -259,7 +261,7 @@ $ pip install -e .[dev]
259261
This command also installs `cmd2-myplugin` "in-place", so the package points to
260262
the source code instead of copying files to the python `site-packages` folder.
261263

262-
All the dependencies now have been installed in the `cmd2-3.8`
264+
All the dependencies now have been installed in the `cmd2-3.9`
263265
virtualenv. If you want to work in other virtualenvs, you'll need to manually
264266
select it, and install again::
265267

plugins/template/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.8, etc. It will also create a .python-version
11+
# cmd2-3.9, 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.8" "3.9" "3.10" "3.11", "3.12")
26+
declare -a pythons=("3.9" "3.10" "3.11", "3.12", "3.13")
2727

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

plugins/template/noxfile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import nox
22

33

4-
@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12'])
4+
@nox.session(python=['3.9', '3.10', '3.11', '3.12', '3.13'])
55
def tests(session):
66
session.install('invoke', './[test]')
77
session.run('invoke', 'pytest', '--junit', '--no-pty')

plugins/template/setup.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
url='https://github.com/python-cmd2/cmd2-plugin-template',
2525
license='MIT',
2626
packages=['cmd2_myplugin'],
27-
python_requires='>=3.8',
27+
python_requires='>=3.9',
2828
install_requires=['cmd2 >= 2, <3'],
2929
setup_requires=['setuptools_scm'],
3030
classifiers=[
@@ -34,11 +34,12 @@
3434
'Topic :: Software Development :: Libraries :: Python Modules',
3535
'Intended Audience :: Developers',
3636
'License :: OSI Approved :: MIT License',
37-
'Programming Language :: Python :: 3.8',
3837
'Programming Language :: Python :: 3.9',
3938
'Programming Language :: Python :: 3.10',
4039
'Programming Language :: Python :: 3.11',
4140
'Programming Language :: Python :: 3.12',
41+
'Programming Language :: Python :: 3.13',
42+
'Programming Language :: Python :: 3.14',
4243
],
4344
# dependencies for development and testing
4445
# $ pip install -e .[dev]

0 commit comments

Comments
 (0)