Skip to content

Add support for Python 3.14 and remove support for Python 3.8 #1419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 22, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ The tables below list all prerequisites along with the minimum required version

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

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14-dev"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4 # https://github.com/actions/checkout
Expand Down
8 changes: 5 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.9.3"
rev: "v0.11.10"
hooks:
- id: ruff-format
args: [--config=pyproject.toml]
- id: ruff-check
args: [--config=pyproject.toml]

- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v3.1.0"
hooks:
- id: prettier
additional_dependencies:
- prettier@3.4.2
- prettier-plugin-toml@2.0.1
- prettier@3.5.3
- prettier-plugin-toml@2.0.5
168 changes: 117 additions & 51 deletions CHANGELOG.md

Large diffs are not rendered by default.

42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,21 @@ when using cmd.
![system schema](https://raw.githubusercontent.com/python-cmd2/cmd2/master/.github/images/graph.drawio.png)

When creating solutions developers have no shortage of tools to create rich and smart user interfaces.
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.
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.
Unfortunately, when CLIs become significantly complex the ease of command discoverability tends to fade quickly.
On the other hand, Web and traditional desktop GUIs are first in class when it comes to easily discovering functionality.
The price we pay for beautifully colored displays is complexity required to aggregate disperate applications into larger systems.
`cmd2` fills the niche between high [ease of command discovery](https://clig.dev/#ease-of-discovery) applications and smart workflow automation systems.

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.
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:
On the other hand, Web and traditional desktop GUIs are first in class when it comes to easily discovering
functionality.
The price we pay for beautifully colored displays is complexity required to aggregate disperate applications into larger
systems.
`cmd2` fills the niche between high [ease of command discovery](https://clig.dev/#ease-of-discovery) applications and
smart workflow automation systems.

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.
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:

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

<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>

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.
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.

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

<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>

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

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

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

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

def do_hello_world(self, _: cmd2.Statement):

def do_hello_world(self, _: cmd2.Statement):
self.poutput('Hello World')


if __name__ == '__main__':
import sys

c = FirstApp()
sys.exit(c.cmdloop())

```

## Found a bug?

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:
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:

- `cmd2` version
- Python version
Expand Down
54 changes: 40 additions & 14 deletions cmd2/argparse_custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,9 @@ def __init__(
conflict_handler: str = 'error',
add_help: bool = True,
allow_abbrev: bool = True,
exit_on_error: bool = True,
suggest_on_error: bool = False,
color: bool = False,
*,
ap_completer_type: Optional[Type['ArgparseCompleter']] = None,
) -> None:
Expand All @@ -1262,20 +1265,43 @@ def __init__(
behavior on this parser. If this is None or not present, then cmd2 will use
argparse_completer.DEFAULT_AP_COMPLETER when tab completing this parser's arguments
"""
super(Cmd2ArgumentParser, self).__init__(
prog=prog,
usage=usage,
description=description,
epilog=epilog,
parents=parents if parents else [],
formatter_class=formatter_class, # type: ignore[arg-type]
prefix_chars=prefix_chars,
fromfile_prefix_chars=fromfile_prefix_chars,
argument_default=argument_default,
conflict_handler=conflict_handler,
add_help=add_help,
allow_abbrev=allow_abbrev,
)
# TODO: CHANGE so if Python >= 3.14 new args are passed
if sys.version_info[1] >= 14:
# Python >= 3.14 so pass new arguments to parent argparse.ArgumentParser class
super(Cmd2ArgumentParser, self).__init__(
prog=prog,
usage=usage,
description=description,
epilog=epilog,
parents=parents if parents else [],
formatter_class=formatter_class, # type: ignore[arg-type]
prefix_chars=prefix_chars,
fromfile_prefix_chars=fromfile_prefix_chars,
argument_default=argument_default,
conflict_handler=conflict_handler,
add_help=add_help,
allow_abbrev=allow_abbrev,
exit_on_error=exit_on_error,
suggest_on_error=suggest_on_error,
color=color,
)
else:
# Python < 3.14, so don't pass new arguments to parent argparse.ArgumentParser class
super(Cmd2ArgumentParser, self).__init__(
prog=prog,
usage=usage,
description=description,
epilog=epilog,
parents=parents if parents else [],
formatter_class=formatter_class, # type: ignore[arg-type]
prefix_chars=prefix_chars,
fromfile_prefix_chars=fromfile_prefix_chars,
argument_default=argument_default,
conflict_handler=conflict_handler,
add_help=add_help,
allow_abbrev=allow_abbrev,
exit_on_error=exit_on_error,
)

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

Expand Down
30 changes: 21 additions & 9 deletions docs/overview/installation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Installation Instructions

`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:
`cmd2` works on Linux, macOS, and Windows. It requires Python 3.9 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:

```shell
$ pip install cmd2
Expand All @@ -16,7 +17,9 @@ $ pip install cmd2

## Prerequisites

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:
If you have Python 3 >=3.9 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:

On Linux or OS X:

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

## Install from PyPI {: #pip_install }

[pip](https://pypi.org/project/pip) is the recommended installer. Installing packages from [PyPI](https://pypi.org) with pip is easy:
[pip](https://pypi.org/project/pip) is the recommended installer. Installing packages from [PyPI](https://pypi.org) with
pip is easy:

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

## Install from GitHub {: #github }

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

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

## Install from Debian or Ubuntu repos

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.
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.

For Python 3:

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

## readline Considerations

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.
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.

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

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`.
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`.

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:
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:

```shell
$ pip install -U gnureadline
Expand Down
4 changes: 2 additions & 2 deletions plugins/ext_test/build-pyenvs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# version numbers are: major.minor.patch
#
# this script will delete and recreate existing virtualenvs named
# cmd2-3.8, etc. It will also create a .python-version
# cmd2-3.9, etc. It will also create a .python-version
#
# Prerequisites:
# - *nix-ish environment like macOS or Linux
Expand All @@ -23,7 +23,7 @@
# virtualenvs will be added to '.python-version'. Feel free to modify
# this list, but note that this script intentionally won't install
# dev, rc, or beta python releases
declare -a pythons=("3.8" "3.9", "3.10", "3.11", "3.12")
declare -a pythons=("3.9", "3.10", "3.11", "3.12", "3.13")

# function to find the latest patch of a minor version of python
function find_latest_version {
Expand Down
2 changes: 1 addition & 1 deletion plugins/ext_test/noxfile.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import nox


@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12'])
@nox.session(python=['3.9', '3.10', '3.11', '3.12', '3.13'])
def tests(session):
session.install('invoke', './[test]')
session.run('invoke', 'pytest', '--junit', '--no-pty')
5 changes: 3 additions & 2 deletions plugins/ext_test/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
license='MIT',
package_data=PACKAGE_DATA,
packages=['cmd2_ext_test'],
python_requires='>=3.8',
python_requires='>=3.9',
install_requires=['cmd2 >= 2, <3'],
setup_requires=['setuptools >= 42', 'setuptools_scm >= 3.4'],
classifiers=[
Expand All @@ -43,11 +43,12 @@
'Topic :: Software Development :: Libraries :: Python Modules',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
],
# dependencies for development and testing
# $ pip install -e .[dev]
Expand Down
4 changes: 3 additions & 1 deletion plugins/template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ and an example app which uses the plugin:
import cmd2
import cmd2_myplugin


class Example(cmd2_myplugin.MyPlugin, cmd2.Cmd):
"""An class to show how to use a plugin"""

def __init__(self, *args, **kwargs):
# code placed here runs before cmd2.Cmd or
# any plugins initialize
Expand Down Expand Up @@ -259,7 +261,7 @@ $ pip install -e .[dev]
This command also installs `cmd2-myplugin` "in-place", so the package points to
the source code instead of copying files to the python `site-packages` folder.

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

Expand Down
4 changes: 2 additions & 2 deletions plugins/template/build-pyenvs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# version numbers are: major.minor.patch
#
# this script will delete and recreate existing virtualenvs named
# cmd2-3.8, etc. It will also create a .python-version
# cmd2-3.9, etc. It will also create a .python-version
#
# Prerequisites:
# - *nix-ish environment like macOS or Linux
Expand All @@ -23,7 +23,7 @@
# virtualenvs will be added to '.python-version'. Feel free to modify
# this list, but note that this script intentionally won't install
# dev, rc, or beta python releases
declare -a pythons=("3.8" "3.9" "3.10" "3.11", "3.12")
declare -a pythons=("3.9" "3.10" "3.11", "3.12", "3.13")

# function to find the latest patch of a minor version of python
function find_latest_version {
Expand Down
2 changes: 1 addition & 1 deletion plugins/template/noxfile.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import nox


@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12'])
@nox.session(python=['3.9', '3.10', '3.11', '3.12', '3.13'])
def tests(session):
session.install('invoke', './[test]')
session.run('invoke', 'pytest', '--junit', '--no-pty')
5 changes: 3 additions & 2 deletions plugins/template/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
url='https://github.com/python-cmd2/cmd2-plugin-template',
license='MIT',
packages=['cmd2_myplugin'],
python_requires='>=3.8',
python_requires='>=3.9',
install_requires=['cmd2 >= 2, <3'],
setup_requires=['setuptools_scm'],
classifiers=[
Expand All @@ -34,11 +34,12 @@
'Topic :: Software Development :: Libraries :: Python Modules',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
],
# dependencies for development and testing
# $ pip install -e .[dev]
Expand Down
Loading
Loading