From 52f8ab67798aeb188cde712f641b4d2fc24bdbd6 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 12 Sep 2024 15:46:27 -0400 Subject: [PATCH 1/2] Remove support for Python 3.7. Add unit testing for Python 3.12. Remove deprecated features. --- .github/CONTRIBUTING.md | 50 +++++++++++----------- .github/workflows/ci.yml | 2 +- .github/workflows/doc.yml | 2 +- .github/workflows/format.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/mypy.yml | 2 +- CHANGELOG.md | 5 ++- README.md | 18 ++++---- cmd2/argparse_custom.py | 27 +++--------- cmd2/cmd2.py | 13 ++---- cmd2/decorators.py | 12 ++---- cmd2/history.py | 6 +-- docs/features/argument_processing.rst | 5 +-- docs/overview/installation.rst | 4 +- noxfile.py | 6 +-- plugins/ext_test/build-pyenvs.sh | 4 +- plugins/ext_test/cmd2_ext_test/__init__.py | 8 +--- plugins/ext_test/noxfile.py | 2 +- plugins/ext_test/setup.py | 4 +- plugins/template/README.md | 18 +++----- plugins/template/build-pyenvs.sh | 4 +- plugins/template/cmd2_myplugin/__init__.py | 8 +--- plugins/template/noxfile.py | 2 +- plugins/template/setup.py | 4 +- setup.py | 4 +- tests/test_cmd2.py | 18 -------- tests/test_transcript.py | 2 +- 27 files changed, 88 insertions(+), 146 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index abd05ccd..72a063cb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -10,7 +10,7 @@ We welcome pull requests from cmd2 users and seasoned Python developers alike! F Remember to feel free to ask for help by leaving a comment within the Issue. -Working on your first pull request? You can learn how from this *free* series +Working on your first pull request? You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). ###### If you've found a bug that is not on the board, [follow these steps](README.md#found-a-bug). @@ -47,7 +47,7 @@ The tables below list all prerequisites along with the minimum required version | Prerequisite | Minimum Version | | --------------------------------------------------- |-----------------| -| [python](https://www.python.org/downloads/) | `3.7` | +| [python](https://www.python.org/downloads/) | `3.8` | | [pyperclip](https://github.com/asweigart/pyperclip) | `1.6` | | [setuptools](https://pypi.org/project/setuptools/) | `34.4` | | [wcwidth](https://pypi.python.org/pypi/wcwidth) | `0.1.7` | @@ -81,17 +81,17 @@ $ pip freeze | grep pyperclip If your versions are lower than the prerequisite versions, you should update. -If you do not already have Python installed on your machine, we recommend using the -[Anaconda](https://www.continuum.io/downloads) distribution because it provides an excellent out-of-the-box install on -all platforms (Windows, Mac, and Linux) and because it supports having multiple Python environments (versions of Python) +If you do not already have Python installed on your machine, we recommend using the +[Anaconda](https://www.continuum.io/downloads) distribution because it provides an excellent out-of-the-box install on +all platforms (Windows, Mac, and Linux) and because it supports having multiple Python environments (versions of Python) installed simultaneously. ### Forking the project #### Setting up your system -1. Install [Git](https://git-scm.com/) or your favorite Git client. If you aren't comfortable with Git at the -command-line, then both [SmartGit](http://www.syntevo.com/smartgit/) and [GitKraken](https://www.gitkraken.com) are +1. Install [Git](https://git-scm.com/) or your favorite Git client. If you aren't comfortable with Git at the +command-line, then both [SmartGit](http://www.syntevo.com/smartgit/) and [GitKraken](https://www.gitkraken.com) are excellent cross-platform graphical Git clients. 2. (Optional) [Set up an SSH key](https://help.github.com/articles/generating-an-ssh-key/) for GitHub. 3. Create a parent projects directory on your system. For this guide, it will be assumed that it is `~/src`. @@ -99,14 +99,14 @@ excellent cross-platform graphical Git clients. #### Forking cmd2 1. Go to the top-level cmd2 repository: -2. Click the "Fork" button in the upper right hand corner of the interface +2. Click the "Fork" button in the upper right hand corner of the interface ([more details here](https://help.github.com/articles/fork-a-repo/)) 3. After the repository has been forked, you will be taken to your copy of the cmd2 repo at `yourUsername/cmd2` #### Cloning your fork 1. Open a terminal / command line / Bash shell in your projects directory (_e.g.: `~/src/`_) -2. Clone your fork of cmd2, making sure to replace `yourUsername` with your GitHub username. This will download the +2. Clone your fork of cmd2, making sure to replace `yourUsername` with your GitHub username. This will download the entire cmd2 repo to your projects directory. ```sh @@ -164,13 +164,13 @@ Do this prior to every time you create a branch for a PR: ### Creating a branch -Before you start working, you will need to create a separate branch specific to the issue or feature you're working on. +Before you start working, you will need to create a separate branch specific to the issue or feature you're working on. You will push your work to this branch. #### Naming your branch -Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature -you are attempting to add. For example `fix/script-files` would be a branch where you fix something specific to script +Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature +you are attempting to add. For example `fix/script-files` would be a branch where you fix something specific to script files. #### Adding your branch @@ -191,22 +191,22 @@ $ git push origin [name_of_your_new_branch] ### Setting up for cmd2 development -For doing cmd2 development, it is recommended you create a virtual environment using Conda or Virtualenv and install the +For doing cmd2 development, it is recommended you create a virtual environment using Conda or Virtualenv and install the package from the source. #### Create a new environment for cmd2 using Pipenv -`cmd2` has support for using [Pipenv](https://docs.pipenv.org/en/latest/) for development. +`cmd2` has support for using [Pipenv](https://docs.pipenv.org/en/latest/) for development. `Pipenv` essentially combines the features of `pip` and `virtualenv` into a single tool. `cmd2` contains a Pipfile which - makes it extremely easy to setup a `cmd2` development environment using `pipenv`. + makes it extremely easy to setup a `cmd2` development environment using `pipenv`. -To create a virtual environment and install everything needed for `cmd2` development using `pipenv`, do the following +To create a virtual environment and install everything needed for `cmd2` development using `pipenv`, do the following from a GitHub checkout: ```sh pipenv install --dev ``` -To create a new virtualenv, using a specific version of Python you have installed (and on your PATH), use the +To create a new virtualenv, using a specific version of Python you have installed (and on your PATH), use the --python VERSION flag, like so: ```sh pipenv install --dev --python 3.8 @@ -219,8 +219,8 @@ pipenv shell #### Create a new environment for cmd2 using Conda ```sh -$ conda create -n cmd2_py37 python=3.7 -$ conda activate cmd2_py37 +$ conda create -n cmd2_py38 python=3.8 +$ conda activate cmd2_py38 ``` #### Create a new environment for cmd using Virtualenv @@ -233,15 +233,15 @@ pyenv versions # Install python version defined pyenv install 3.8.2 ``` -With the Python version installed, you can set the virtualenv properly. +With the Python version installed, you can set the virtualenv properly. ```sh $ cd ~/src/cmd2 -$ virtualenv -p $(pyenv root)/versions/3.8.2/ cmd_py38 +$ virtualenv -p $(pyenv root)/versions/3.8.2/ cmd_py38 $ source ~/src/cmd2/bin/activate ``` -Assuming you cloned the repository to `~/src/cmd2` you can install cmd2 in +Assuming you cloned the repository to `~/src/cmd2` you can install cmd2 in [editable mode](https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs). Changes to the source code are immediately available when the python interpreter imports `cmd2`, there is no need to re-install the module after every change. This @@ -326,9 +326,9 @@ served (usually [http://localhost:8000](http://localhost:8000)). ### Static code analysis -You should have some sort of [PEP 8](https://www.python.org/dev/peps/pep-0008/)-based linting running in your editor or +You should have some sort of [PEP 8](https://www.python.org/dev/peps/pep-0008/)-based linting running in your editor or IDE or at the command line before you commit code. `cmd2` uses [flake8](http://flake8.pycqa.org/en/latest/) as part of -its continuous integration (CI) process. [pylint](https://www.pylint.org) is another good Python linter which can be +its continuous integration (CI) process. [pylint](https://www.pylint.org) is another good Python linter which can be run at the command line but also can integrate with many IDEs and editors. > Please do not ignore any linting errors in code you write or modify, as they are meant to **help** you and to ensure a clean and simple code base. Don't worry about linting errors in code you don't touch though - cleaning up the legacy code is a work in progress. @@ -579,7 +579,7 @@ mostly automated. The manual steps are all git operations. Here's the checklist: 1. Make sure all the unit tests pass with `invoke pytest` or `py.test` 1. Make sure latest year in `LICENSE` matches current year 1. Make sure `CHANGELOG.md` describes the version and has the correct release date -1. Add a git tag representing the version number using ``invoke tag x.y.z`` +1. Add a git tag representing the version number using ``invoke tag x.y.z`` * Where x, y, and z are all small non-negative integers 1. (Optional) Run `invoke pypi-test` to clean, build, and upload a new release to [Test PyPi](https://test.pypi.org) 1. Run `invoke pypi` to clean, build, and upload a new release to [PyPi](https://pypi.org/) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0e16e694..b35ac514 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index 77f18218..9605605f 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ["3.11"] + python-version: ["3.12"] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 3a8d2ab7..f168f598 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ["3.11"] + python-version: ["3.12"] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9eb8ebdc..f7a97227 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ["3.11"] + python-version: ["3.12"] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index c5f2aca6..727ed43d 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ["3.11"] + python-version: ["3.12"] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/CHANGELOG.md b/CHANGELOG.md index e65c6b40..dac58426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,13 @@ ## 2.5.0 (TBD) * Breaking Change - * `cmd2` 2.5 supports Python 3.7+ (removed support for Python 3.6) + * `cmd2` 2.5 supports Python 3.8+ (removed support for Python 3.6 and 3.7) * Enhancements * Removed dependency on `attrs` and replaced with [dataclasses](https://docs.python.org/3/library/dataclasses.html) * add `allow_clipboard` initialization parameter and attribute to disable ability to add output to the operating system clipboard + * Updated unit tests to be Python 3.12 compliant. +* Deletions (potentially breaking changes) + * Removed `apply_style` from `Cmd.pwarning()`. ## 2.4.3 (January 27, 2023) diff --git a/README.md b/README.md index 09e083df..d15d58eb 100755 --- a/README.md +++ b/README.md @@ -32,14 +32,14 @@ The developers toolbox ![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. -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. +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. +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. +`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. +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 @@ -78,7 +78,7 @@ On all operating systems, the latest stable version of `cmd2` can be installed u pip install -U cmd2 ``` -cmd2 works with Python 3.7+ on Windows, macOS, and Linux. It is pure Python code with few 3rd-party dependencies. +cmd2 works with Python 3.8+ 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 @@ -97,7 +97,7 @@ The best way to learn the cmd2 api is to delve into the example applications loc Tutorials --------- -* PyOhio 2019 presentation: +* PyOhio 2019 presentation: * [video](https://www.youtube.com/watch?v=pebeWrTqIIw) * [slides](https://github.com/python-cmd2/talks/blob/master/PyOhio_2019/cmd2-PyOhio_2019.pdf) * [example code](https://github.com/python-cmd2/talks/tree/master/PyOhio_2019/examples) @@ -161,4 +161,4 @@ Projects using cmd2 Possibly defunct but still good examples * [JSShell](https://github.com/Den1al/JSShell) -* [FLASHMINGO](https://github.com/fireeye/flashmingo) +* [FLASHMINGO](https://github.com/fireeye/flashmingo) diff --git a/cmd2/argparse_custom.py b/cmd2/argparse_custom.py index 2371fa54..78a492f6 100644 --- a/cmd2/argparse_custom.py +++ b/cmd2/argparse_custom.py @@ -257,12 +257,14 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens) List, NoReturn, Optional, + Protocol, Sequence, Set, Tuple, Type, Union, cast, + runtime_checkable, ) from . import ( @@ -270,19 +272,6 @@ def my_completer(self, text, line, begidx, endidx, arg_tokens) constants, ) -try: - from typing import ( - Protocol, - runtime_checkable, - ) -except ImportError: - # Remove these imports when we no longer support Python 3.7 - from typing_extensions import ( # type: ignore[assignment] - Protocol, - runtime_checkable, - ) - - if TYPE_CHECKING: # pragma: no cover from .argparse_completer import ( ArgparseCompleter, @@ -352,8 +341,7 @@ class ChoicesProviderFuncBase(Protocol): Function that returns a list of choices in support of tab completion """ - def __call__(self) -> List[str]: - ... # pragma: no cover + def __call__(self) -> List[str]: ... # pragma: no cover @runtime_checkable @@ -362,8 +350,7 @@ class ChoicesProviderFuncWithTokens(Protocol): Function that returns a list of choices in support of tab completion and accepts a dictionary of prior arguments. """ - def __call__(self, *, arg_tokens: Dict[str, List[str]] = {}) -> List[str]: - ... # pragma: no cover + def __call__(self, *, arg_tokens: Dict[str, List[str]] = {}) -> List[str]: ... # pragma: no cover ChoicesProviderFunc = Union[ChoicesProviderFuncBase, ChoicesProviderFuncWithTokens] @@ -381,8 +368,7 @@ def __call__( line: str, begidx: int, endidx: int, - ) -> List[str]: - ... # pragma: no cover + ) -> List[str]: ... # pragma: no cover @runtime_checkable @@ -400,8 +386,7 @@ def __call__( endidx: int, *, arg_tokens: Dict[str, List[str]] = {}, - ) -> List[str]: - ... # pragma: no cover + ) -> List[str]: ... # pragma: no cover CompleterFunc = Union[CompleterFuncBase, CompleterFuncWithTokens] diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index c5c0db78..a7f5b64d 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -780,7 +780,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None: methods = inspect.getmembers( cmdset, - predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] + predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type, var-annotated] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) @@ -811,7 +811,7 @@ def unregister_command_set(self, cmdset: CommandSet) -> None: def _check_uninstallable(self, cmdset: CommandSet) -> None: methods = inspect.getmembers( cmdset, - predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type] + predicate=lambda meth: isinstance(meth, Callable) # type: ignore[arg-type, var-annotated] and hasattr(meth, '__name__') and meth.__name__.startswith(COMMAND_FUNC_PREFIX), ) @@ -1223,7 +1223,6 @@ def pwarning( msg: Any = '', *, end: str = '\n', - apply_style: bool = True, paged: bool = False, chop: bool = False, ) -> None: @@ -1231,16 +1230,10 @@ def pwarning( :param msg: object to print :param end: string appended after the end of the message, default a newline - :param apply_style: - If True, then ansi.style_warning will be applied to the message text. Set to False in cases - where the message text already has the desired style. Defaults to True. - - .. deprecated: 2.4.4 - Use :meth:`~cmd2.Cmd.print_to` instead to print to stderr without style applied. :param paged: If True, pass the output through the configured pager. :param chop: If paged is True, True to truncate long lines or False to wrap long lines. """ - self.print_to(sys.stderr, msg, end=end, style=ansi.style_warning if apply_style else None, paged=paged, chop=chop) + self.print_to(sys.stderr, msg, end=end, style=ansi.style_warning, paged=paged, chop=chop) def pfailure( self, diff --git a/cmd2/decorators.py b/cmd2/decorators.py index 3a163fda..361b71ac 100644 --- a/cmd2/decorators.py +++ b/cmd2/decorators.py @@ -266,8 +266,7 @@ def with_argparser( ns_provider: Optional[Callable[..., argparse.Namespace]] = None, preserve_quotes: bool = False, with_unknown_args: bool = False, -) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]: - ... # pragma: no cover +) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]: ... # pragma: no cover @overload @@ -277,8 +276,7 @@ def with_argparser( ns_provider: Optional[Callable[..., argparse.Namespace]] = None, preserve_quotes: bool = False, with_unknown_args: bool = False, -) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]: - ... # pragma: no cover +) -> Callable[[ArgparseCommandFunc[CommandParent]], RawCommandFuncOptionalBoolReturn[CommandParent]]: ... # pragma: no cover def with_argparser( @@ -418,8 +416,7 @@ def as_subcommand_to( *, help: Optional[str] = None, aliases: Optional[List[str]] = None, -) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: - ... # pragma: no cover +) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: ... # pragma: no cover @overload @@ -430,8 +427,7 @@ def as_subcommand_to( *, help: Optional[str] = None, aliases: Optional[List[str]] = None, -) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: - ... # pragma: no cover +) -> Callable[[ArgparseCommandFunc[CommandParent]], ArgparseCommandFunc[CommandParent]]: ... # pragma: no cover def as_subcommand_to( diff --git a/cmd2/history.py b/cmd2/history.py index a7d6baff..c79a19dd 100644 --- a/cmd2/history.py +++ b/cmd2/history.py @@ -154,12 +154,10 @@ def _zero_based_index(self, onebased: Union[int, str]) -> int: return result @overload - def append(self, new: HistoryItem) -> None: - ... # pragma: no cover + def append(self, new: HistoryItem) -> None: ... # pragma: no cover @overload - def append(self, new: Statement) -> None: - ... # pragma: no cover + def append(self, new: Statement) -> None: ... # pragma: no cover def append(self, new: Union[Statement, HistoryItem]) -> None: """Append a new statement to the end of the History list. diff --git a/docs/features/argument_processing.rst b/docs/features/argument_processing.rst index 67b94878..16031200 100644 --- a/docs/features/argument_processing.rst +++ b/docs/features/argument_processing.rst @@ -78,9 +78,8 @@ Here's what it looks like:: .. warning:: It is important that each command which uses the ``@with_argparser`` - decorator be passed a unique instance of a parser. This limitation is due - to bugs in CPython prior to Python 3.7 which make it impossible to make a - deep copy of an instance of a ``argparse.ArgumentParser``. + decorator be passed a unique instance of a parser since command-specific + changes could be made to it. .. note:: diff --git a/docs/overview/installation.rst b/docs/overview/installation.rst index 743e0aca..841807c8 100644 --- a/docs/overview/installation.rst +++ b/docs/overview/installation.rst @@ -7,7 +7,7 @@ Installation Instructions .. _setuptools: https://pypi.org/project/setuptools .. _PyPI: https://pypi.org -``cmd2`` works on Linux, macOS, and Windows. It requires Python 3.7 or +``cmd2`` works on Linux, macOS, and Windows. It requires Python 3.8 or higher, pip_, and setuptools_. If you've got all that, then you can just: .. code-block:: shell @@ -30,7 +30,7 @@ higher, pip_, and setuptools_. If you've got all that, then you can just: Prerequisites ------------- -If you have Python 3 >=3.7 installed from `python.org +If you have Python 3 >=3.8 installed from `python.org `_, you will already have pip_ and setuptools_, but may need to upgrade to the latest versions: diff --git a/noxfile.py b/noxfile.py index f7a705fb..9ec2ce16 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,7 +1,7 @@ import nox -@nox.session(python=['3.11']) +@nox.session(python=['3.12']) def docs(session): session.install( 'sphinx', @@ -17,7 +17,7 @@ def docs(session): ) -@nox.session(python=['3.7', '3.8', '3.9', '3.10', '3.11']) +@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12']) @nox.parametrize('plugin', [None, 'ext_test', 'template', 'coverage']) def tests(session, plugin): if plugin is None: @@ -41,7 +41,7 @@ def tests(session, plugin): ) -@nox.session(python=['3.8', '3.9', '3.10', '3.11']) +@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12']) @nox.parametrize('step', ['mypy', 'flake8']) def validate(session, step): session.install('invoke', './[validate]') diff --git a/plugins/ext_test/build-pyenvs.sh b/plugins/ext_test/build-pyenvs.sh index 572db568..20f5c8d4 100644 --- a/plugins/ext_test/build-pyenvs.sh +++ b/plugins/ext_test/build-pyenvs.sh @@ -8,7 +8,7 @@ # version numbers are: major.minor.patch # # this script will delete and recreate existing virtualenvs named -# cmd2-3.7, etc. It will also create a .python-version +# cmd2-3.8, etc. It will also create a .python-version # # Prerequisites: # - *nix-ish environment like macOS or Linux @@ -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.7" "3.8" "3.9", "3.10", "3.11") +declare -a pythons=("3.8" "3.9", "3.10", "3.11", "3.12") # function to find the latest patch of a minor version of python function find_latest_version { diff --git a/plugins/ext_test/cmd2_ext_test/__init__.py b/plugins/ext_test/cmd2_ext_test/__init__.py index b30c949d..b154f0ed 100644 --- a/plugins/ext_test/cmd2_ext_test/__init__.py +++ b/plugins/ext_test/cmd2_ext_test/__init__.py @@ -5,13 +5,7 @@ Allows developers to exercise their cmd2 application using the PyScript interface """ -try: - # For python 3.8 and later - import importlib.metadata as importlib_metadata -except ImportError: # pragma: no cover - # Remove this import when we no longer support Python 3.7 - # MyPy Issue # 1153 causes a spurious error that must be ignored - import importlib_metadata # type: ignore +import importlib.metadata as importlib_metadata try: __version__ = importlib_metadata.version(__name__) diff --git a/plugins/ext_test/noxfile.py b/plugins/ext_test/noxfile.py index 9872e193..25a95067 100644 --- a/plugins/ext_test/noxfile.py +++ b/plugins/ext_test/noxfile.py @@ -1,7 +1,7 @@ import nox -@nox.session(python=['3.7', '3.8', '3.9', '3.10', '3.11']) +@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12']) def tests(session): session.install('invoke', './[test]') session.run('invoke', 'pytest', '--junit', '--no-pty') diff --git a/plugins/ext_test/setup.py b/plugins/ext_test/setup.py index 644d2677..45110413 100644 --- a/plugins/ext_test/setup.py +++ b/plugins/ext_test/setup.py @@ -33,7 +33,7 @@ license='MIT', package_data=PACKAGE_DATA, packages=['cmd2_ext_test'], - python_requires='>=3.7', + python_requires='>=3.8', install_requires=['cmd2 >= 2, <3'], setup_requires=['setuptools >= 42', 'setuptools_scm >= 3.4'], classifiers=[ @@ -43,11 +43,11 @@ 'Topic :: Software Development :: Libraries :: Python Modules', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3.7', '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', ], # dependencies for development and testing # $ pip install -e .[dev] diff --git a/plugins/template/README.md b/plugins/template/README.md index f7f6d057..8617acd7 100644 --- a/plugins/template/README.md +++ b/plugins/template/README.md @@ -210,7 +210,7 @@ tiered testing strategy to accomplish this objective. - [pytest](https://pytest.org) runs the unit tests - [nox](https://nox.thea.codes/en/stable/) runs the unit tests on multiple versions of python -- [GitHub Actions](https://github.com/features/actions) runs the tests on the various +- [GitHub Actions](https://github.com/features/actions) runs the tests on the various supported platforms This plugin template is set up to use the same strategy. @@ -231,17 +231,15 @@ automates the creation of these environments. If you prefer to create these virtualenvs by hand, do the following: ``` $ cd cmd2_abbrev -$ pyenv install 3.7.0 -$ pyenv virtualenv -p python3.7 3.7.0 cmd2-3.7 $ pyenv install 3.8.5 $ pyenv virtualenv -p python3.8 3.8.5 cmd2-3.8 $ pyenv install 3.9.0 $ pyenv virtualenv -p python3.9 3.9.0 cmd2-3.9 ``` -Now set pyenv to make all three of those available at the same time: +Now set pyenv to make both of those available at the same time: ``` -$ pyenv local cmd2-3.7 cmd2-3.8 cmd2-3.9 +$ pyenv local cmd2-3.8 cmd2-3.9 ``` Whether you ran the script, or did it by hand, you now have isolated virtualenvs @@ -251,10 +249,8 @@ utilize. | Command | python | virtualenv | | ----------- | ------ | ---------- | -| `python3.7` | 3.7.0 | cmd2-3.7 | | `python3.8` | 3.8.5 | cmd2-3.8 | | `python3.9` | 3.9.0 | cmd2-3.9 | -| `pip3.7` | 3.7.0 | cmd2-3.7 | | `pip3.8` | 3.8.5 | cmd2-3.8 | | `pip3.9` | 3.9.0 | cmd2-3.9 | @@ -268,7 +264,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.7` +All the dependencies now have been installed in the `cmd2-3.8` virtualenv. If you want to work in other virtualenvs, you'll need to manually select it, and install again:: @@ -290,9 +286,9 @@ unit tests found in the `tests` directory. ### Use nox to run unit tests in multiple versions of python -The included `noxfile.py` is setup to run the unit tests in python 3.7, 3.8, -3.9, 3.10, and 3.11 You can run your unit tests in all of these versions of -python by: +The included `noxfile.py` is setup to run the unit tests in python 3.8, 3.9 +3.10, 3.11, and 3.12 You can run your unit tests in all of these versions +of python by: ``` $ nox ``` diff --git a/plugins/template/build-pyenvs.sh b/plugins/template/build-pyenvs.sh index f4084494..4a6e1578 100644 --- a/plugins/template/build-pyenvs.sh +++ b/plugins/template/build-pyenvs.sh @@ -8,7 +8,7 @@ # version numbers are: major.minor.patch # # this script will delete and recreate existing virtualenvs named -# cmd2-3.7, etc. It will also create a .python-version +# cmd2-3.8, etc. It will also create a .python-version # # Prerequisites: # - *nix-ish environment like macOS or Linux @@ -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.7" "3.8" "3.9" "3.10" "3.11") +declare -a pythons=("3.8" "3.9" "3.10" "3.11", "3.12") # function to find the latest patch of a minor version of python function find_latest_version { diff --git a/plugins/template/cmd2_myplugin/__init__.py b/plugins/template/cmd2_myplugin/__init__.py index 394b219b..a35976b7 100644 --- a/plugins/template/cmd2_myplugin/__init__.py +++ b/plugins/template/cmd2_myplugin/__init__.py @@ -5,17 +5,13 @@ An overview of what myplugin does. """ +import importlib.metadata as importlib_metadata + from .myplugin import ( # noqa: F401 MyPluginMixin, empty_decorator, ) -try: - # For python 3.8 and later - import importlib.metadata as importlib_metadata -except ImportError: # pragma: no cover - # Remove this import when we no longer support Python 3.7 - import importlib_metadata try: __version__ = importlib_metadata.version(__name__) except importlib_metadata.PackageNotFoundError: # pragma: no cover diff --git a/plugins/template/noxfile.py b/plugins/template/noxfile.py index 9872e193..25a95067 100644 --- a/plugins/template/noxfile.py +++ b/plugins/template/noxfile.py @@ -1,7 +1,7 @@ import nox -@nox.session(python=['3.7', '3.8', '3.9', '3.10', '3.11']) +@nox.session(python=['3.8', '3.9', '3.10', '3.11', '3.12']) def tests(session): session.install('invoke', './[test]') session.run('invoke', 'pytest', '--junit', '--no-pty') diff --git a/plugins/template/setup.py b/plugins/template/setup.py index 47cc72c2..7e872cd8 100644 --- a/plugins/template/setup.py +++ b/plugins/template/setup.py @@ -24,7 +24,7 @@ url='https://github.com/python-cmd2/cmd2-plugin-template', license='MIT', packages=['cmd2_myplugin'], - python_requires='>=3.7', + python_requires='>=3.8', install_requires=['cmd2 >= 2, <3'], setup_requires=['setuptools_scm'], classifiers=[ @@ -34,11 +34,11 @@ 'Topic :: Software Development :: Libraries :: Python Modules', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3.7', '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', ], # dependencies for development and testing # $ pip install -e .[dev] diff --git a/setup.py b/setup.py index dae53695..004cbee9 100755 --- a/setup.py +++ b/setup.py @@ -28,11 +28,11 @@ License :: OSI Approved :: MIT License Programming Language :: Python Programming Language :: Python :: 3 -Programming Language :: Python :: 3.7 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 :: Implementation :: CPython Topic :: Software Development :: Libraries :: Python Modules """.splitlines(), @@ -105,7 +105,7 @@ package_data=PACKAGE_DATA, packages=['cmd2'], keywords='command prompt console cmd', - python_requires='>=3.7', + python_requires='>=3.8', setup_requires=SETUP_REQUIRES, install_requires=INSTALL_REQUIRES, extras_require=EXTRAS_REQUIRE, diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 054eef6e..5686e9f9 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -2316,24 +2316,6 @@ def test_perror_no_style(base_app, capsys): assert err == msg + end -@with_ansi_style(ansi.AllowStyle.ALWAYS) -def test_pwarning_style(base_app, capsys): - msg = 'testing...' - end = '\n' - base_app.pwarning(msg) - out, err = capsys.readouterr() - assert err == ansi.style_warning(msg) + end - - -@with_ansi_style(ansi.AllowStyle.ALWAYS) -def test_pwarning_no_style(base_app, capsys): - msg = 'testing...' - end = '\n' - base_app.pwarning(msg, apply_style=False) - out, err = capsys.readouterr() - assert err == msg + end - - @with_ansi_style(ansi.AllowStyle.ALWAYS) def test_pexcept_style(base_app, capsys): msg = Exception('testing...') diff --git a/tests/test_transcript.py b/tests/test_transcript.py index 39c87532..ed193a00 100644 --- a/tests/test_transcript.py +++ b/tests/test_transcript.py @@ -260,7 +260,7 @@ def test_generate_transcript_stop(capsys): # strings with zero or one slash or with escaped slashes means no regular # expression present, so the result should just be what re.escape returns. # we don't use static strings in these tests because re.escape behaves - # differently in python 3.7 than in prior versions + # differently in python 3.7+ than in prior versions ('text with no slashes', re.escape('text with no slashes')), ('specials .*', re.escape('specials .*')), ('use 2/3 cup', re.escape('use 2/3 cup')), From 42aed9ca33d145fba0f2b70eb8e58f7b80ea8680 Mon Sep 17 00:00:00 2001 From: Kevin Van Brunt Date: Thu, 12 Sep 2024 16:50:20 -0400 Subject: [PATCH 2/2] Fix validation tests. --- setup.cfg | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 5d42015c..5f26578f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,7 +9,7 @@ addopts = [flake8] count = True -ignore = E203,W503 +ignore = E203,W503,E704 max-complexity = 26 max-line-length = 127 show-source = True diff --git a/setup.py b/setup.py index 004cbee9..ce46c17d 100755 --- a/setup.py +++ b/setup.py @@ -82,7 +82,7 @@ 'validate': [ 'flake8', 'mypy', - 'types-pkg-resources', + 'types-setuptools', ], }