From 06520ea7dc8db4bdbf29c5f6f563dc3ddf655914 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 13:38:22 -0400 Subject: [PATCH 1/8] Fix bug when using ipy with gnureadline and Python 3.13 --- cmd2/cmd2.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index dbd99349..8c45917a 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -128,6 +128,13 @@ StatementParser, shlex_split, ) + +# NOTE: When using gnureadline with Python 3.13, start_ipython needs to be imported before any readline-related stuff +try: + from IPython import start_ipython +except ImportError: + pass + from .rl_utils import ( RlType, rl_escape_prompt, @@ -4629,9 +4636,13 @@ def do_ipy(self, _: argparse.Namespace) -> Optional[bool]: # pragma: no cover # Detect whether IPython is installed try: import traitlets.config.loader as TraitletsLoader # type: ignore[import] - from IPython import ( # type: ignore[import] - start_ipython, - ) + + # Allow users to install ipython from a cmd2 prompt when needed and still have ipy command work + try: + start_ipython # noqa F823 + except NameError: + from IPython import start_ipython + from IPython.terminal.interactiveshell import ( # type: ignore[import] TerminalInteractiveShell, ) From bdc38c42c861b2e831d624d4bc3cc3e9dea5d3e8 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 13:46:41 -0400 Subject: [PATCH 2/8] Fix mypy error with IPython stuff --- .github/workflows/mypy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 6c07f536..8065683b 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -11,13 +11,13 @@ jobs: mypy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 # https://github.com/actions/checkout - - uses: actions/setup-python@v5 # https://github.com/actions/setup-python + - uses: actions/checkout@v4 # https://github.com/actions/checkout + - uses: actions/setup-python@v5 # https://github.com/actions/setup-python with: python-version: 3.13 allow-prereleases: true # Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. # Set fetch-depth: 0 to fetch all history for all branches and tags. fetch-depth: 0 # Needed for setuptools_scm to work correctly - - run: pip install -U --user pip mypy + - run: pip install -U --user pip mypy ipython-stubs - run: mypy . From 9830be5bfdaf2e28b1df03fc7e088d45a1a55803 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 13:52:39 -0400 Subject: [PATCH 3/8] Fix mypy errors when importing stuff from IPython --- .github/workflows/mypy.yml | 2 +- cmd2/cmd2.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index 8065683b..46372c2a 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -19,5 +19,5 @@ jobs: # Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. # Set fetch-depth: 0 to fetch all history for all branches and tags. fetch-depth: 0 # Needed for setuptools_scm to work correctly - - run: pip install -U --user pip mypy ipython-stubs + - run: pip install -U --user pip mypy - run: mypy . diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 8c45917a..a508f305 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -131,7 +131,7 @@ # NOTE: When using gnureadline with Python 3.13, start_ipython needs to be imported before any readline-related stuff try: - from IPython import start_ipython + from IPython import start_ipython # type: ignore[import] except ImportError: pass @@ -4641,7 +4641,7 @@ def do_ipy(self, _: argparse.Namespace) -> Optional[bool]: # pragma: no cover try: start_ipython # noqa F823 except NameError: - from IPython import start_ipython + from IPython import start_ipython # type: ignore[import] from IPython.terminal.interactiveshell import ( # type: ignore[import] TerminalInteractiveShell, From e4bf8238a06343478e4985db61140da6bebe2848 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 13:58:01 -0400 Subject: [PATCH 4/8] Updated CHANGELOG with info on bug fix --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 417d9b42..c7a0dcd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.5.1 (November TBD, 2024) +* Bug Fixes + * Fixed readline bug when using `ipy` command with `gnureadline` and Python 3.13 + ## 2.5.0 (October 23, 2024) * Breaking Change * `cmd2` 2.5 supports Python 3.8+ (removed support for Python 3.6 and 3.7) From fd4f7be2fdaa3e919aaa1b0579506eb37e0498c6 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 14:56:36 -0400 Subject: [PATCH 5/8] Added invoke task for cleaning ruff cache directory --- tasks.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tasks.py b/tasks.py index 1226c6bf..f8120d20 100644 --- a/tasks.py +++ b/tasks.py @@ -365,3 +365,14 @@ def format(context): namespace.add_task(format) + + +@invoke.task() +def ruff_clean(context): + """Remove .ruff_cache directory""" + with context.cd(TASK_ROOT_STR): + dirs = ['.ruff_cache'] + rmrf(dirs) + + +namespace_clean.add_task(ruff_clean, 'ruff') From 4dca320165a94d7de0f7d97ee52cc33f27954578 Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 15:22:58 -0400 Subject: [PATCH 6/8] Added ruff cache clean invoke task to plugins --- plugins/tasks.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/plugins/tasks.py b/plugins/tasks.py index 2717ecf4..c2311e69 100644 --- a/plugins/tasks.py +++ b/plugins/tasks.py @@ -9,7 +9,9 @@ - setuptools >= 39.1.0 """ +import os import pathlib +import shutil import invoke @@ -38,6 +40,23 @@ TASK_ROOT_STR = str(TASK_ROOT) +# shared function +def rmrf(items, verbose=True): + """Silently remove a list of directories or files""" + if isinstance(items, str): + items = [items] + + for item in items: + if verbose: + print("Removing {}".format(item)) + shutil.rmtree(item, ignore_errors=True) + # rmtree doesn't remove bare files + try: + os.remove(item) + except FileNotFoundError: + pass + + @invoke.task(pre=[ext_test_tasks.pytest]) @invoke.task() def pytest(_): @@ -154,3 +173,14 @@ def format(context): namespace.add_task(format) + + +@invoke.task() +def ruff_clean(context): + """Remove .ruff_cache directory""" + with context.cd(TASK_ROOT_STR): + dirs = ['.ruff_cache'] + rmrf(dirs) + + +namespace_clean.add_task(ruff_clean, 'ruff') From a00b6cc5e2ed42c3ab58ba2fa77e51a4d9b13efc Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 17:38:57 -0400 Subject: [PATCH 7/8] Use the "ruff clean" command to clear all ruff cache dirs --- plugins/tasks.py | 30 ------------------------------ tasks.py | 3 +-- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/plugins/tasks.py b/plugins/tasks.py index c2311e69..2717ecf4 100644 --- a/plugins/tasks.py +++ b/plugins/tasks.py @@ -9,9 +9,7 @@ - setuptools >= 39.1.0 """ -import os import pathlib -import shutil import invoke @@ -40,23 +38,6 @@ TASK_ROOT_STR = str(TASK_ROOT) -# shared function -def rmrf(items, verbose=True): - """Silently remove a list of directories or files""" - if isinstance(items, str): - items = [items] - - for item in items: - if verbose: - print("Removing {}".format(item)) - shutil.rmtree(item, ignore_errors=True) - # rmtree doesn't remove bare files - try: - os.remove(item) - except FileNotFoundError: - pass - - @invoke.task(pre=[ext_test_tasks.pytest]) @invoke.task() def pytest(_): @@ -173,14 +154,3 @@ def format(context): namespace.add_task(format) - - -@invoke.task() -def ruff_clean(context): - """Remove .ruff_cache directory""" - with context.cd(TASK_ROOT_STR): - dirs = ['.ruff_cache'] - rmrf(dirs) - - -namespace_clean.add_task(ruff_clean, 'ruff') diff --git a/tasks.py b/tasks.py index f8120d20..2fa88cf5 100644 --- a/tasks.py +++ b/tasks.py @@ -371,8 +371,7 @@ def format(context): def ruff_clean(context): """Remove .ruff_cache directory""" with context.cd(TASK_ROOT_STR): - dirs = ['.ruff_cache'] - rmrf(dirs) + context.run("ruff clean") namespace_clean.add_task(ruff_clean, 'ruff') From 3790f17129b80902d8dd1315459843ccb62d657e Mon Sep 17 00:00:00 2001 From: Todd Leonhardt Date: Sat, 2 Nov 2024 17:51:32 -0400 Subject: [PATCH 8/8] Made cmd2/parsing.py non-executable and examples executable --- cmd2/parsing.py | 0 examples/argparse_completion.py | 0 examples/default_categories.py | 0 examples/modular_commands_basic.py | 0 examples/modular_commands_dynamic.py | 0 examples/modular_commands_main.py | 0 examples/modular_subcommands.py | 0 examples/read_input.py | 0 8 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 cmd2/parsing.py mode change 100644 => 100755 examples/argparse_completion.py mode change 100644 => 100755 examples/default_categories.py mode change 100644 => 100755 examples/modular_commands_basic.py mode change 100644 => 100755 examples/modular_commands_dynamic.py mode change 100644 => 100755 examples/modular_commands_main.py mode change 100644 => 100755 examples/modular_subcommands.py mode change 100644 => 100755 examples/read_input.py diff --git a/cmd2/parsing.py b/cmd2/parsing.py old mode 100755 new mode 100644 diff --git a/examples/argparse_completion.py b/examples/argparse_completion.py old mode 100644 new mode 100755 diff --git a/examples/default_categories.py b/examples/default_categories.py old mode 100644 new mode 100755 diff --git a/examples/modular_commands_basic.py b/examples/modular_commands_basic.py old mode 100644 new mode 100755 diff --git a/examples/modular_commands_dynamic.py b/examples/modular_commands_dynamic.py old mode 100644 new mode 100755 diff --git a/examples/modular_commands_main.py b/examples/modular_commands_main.py old mode 100644 new mode 100755 diff --git a/examples/modular_subcommands.py b/examples/modular_subcommands.py old mode 100644 new mode 100755 diff --git a/examples/read_input.py b/examples/read_input.py old mode 100644 new mode 100755