From 6a2a6eb0ac62a0090b274ae7a26cc1df996d5523 Mon Sep 17 00:00:00 2001 From: Roland Walker Date: Sat, 21 Feb 2026 08:49:08 -0500 Subject: [PATCH] better handle arguments to "system cd" * Use shlex.split to handle quoted or backslash-escaped arguments correctly. Previously the directory argument had to be a single word. * Use os.getcwd() instead of executing external "pwd". * Improve error messages. --- changelog.md | 1 + mycli/packages/special/utils.py | 23 +++++++++++++++++------ test/test_sqlexecute.py | 27 ++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 9fc4f094..496cf134 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ Features Bug Fixes --------- * Let interactive changes to the prompt format respect dynamically-computed values. +* Better handle arguments to `system cd`. 1.56.0 (2026/02/23) diff --git a/mycli/packages/special/utils.py b/mycli/packages/special/utils.py index eedec11e..88002a89 100644 --- a/mycli/packages/special/utils.py +++ b/mycli/packages/special/utils.py @@ -1,10 +1,13 @@ import logging import os -import subprocess +import shlex +import click import pymysql from pymysql.cursors import Cursor +from mycli.compat import WIN + logger = logging.getLogger(__name__) CACHED_SSL_VERSION: dict[tuple, str | None] = {} @@ -13,13 +16,21 @@ def handle_cd_command(arg: str) -> tuple[bool, str | None]: """Handles a `cd` shell command by calling python's os.chdir.""" CD_CMD = "cd" - tokens = arg.split(CD_CMD + " ") - directory = tokens[-1] if len(tokens) > 1 else None - if not directory: - return False, "No folder name was provided." + tokens: list[str] = [] + try: + tokens = shlex.split(arg, posix=not WIN) + except ValueError: + return False, 'Cannot parse cd command.' + if not tokens: + return False, 'Not a cd command.' + if not tokens[0].lower() == CD_CMD: + return False, 'Not a cd command.' + if len(tokens) != 2: + return False, 'Exactly one directory name must be provided.' + directory = tokens[1] try: os.chdir(directory) - subprocess.call(["pwd"]) + click.echo(os.getcwd(), err=True) return True, None except OSError as e: return False, e.strerror diff --git a/test/test_sqlexecute.py b/test/test_sqlexecute.py index 9abe3b22..301e14be 100644 --- a/test/test_sqlexecute.py +++ b/test/test_sqlexecute.py @@ -222,7 +222,32 @@ def test_special_command(executor): @dbtest def test_cd_command_without_a_folder_name(executor): results = run(executor, "system cd") - assert_result_equal(results, status="No folder name was provided.") + assert_result_equal(results, status="Exactly one directory name must be provided.") + + +@dbtest +def test_cd_command_with_one_nonexistent_folder_name(executor): + results = run(executor, 'system cd nonexistent_folder_name') + assert_result_equal(results, status='No such file or directory') + + +@dbtest +def test_cd_command_with_one_real_folder_name(executor): + results = run(executor, 'system cd screenshots') + # todo would be better to capture stderr but there was a problem with capsys + assert results[0]['status'] == '' + + +@dbtest +def test_cd_command_with_two_folder_names(executor): + results = run(executor, "system cd one two") + assert_result_equal(results, status='Exactly one directory name must be provided.') + + +@dbtest +def test_cd_command_unbalanced(executor): + results = run(executor, "system cd 'one") + assert_result_equal(results, status='Cannot parse cd command.') @dbtest