]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-135883: Fix sqlite3 CLI history scrolling with colored prompts (#135884)
authorTan Long <tanloong@foxmail.com>
Tue, 3 Mar 2026 18:48:03 +0000 (02:48 +0800)
committerGitHub <noreply@github.com>
Tue, 3 Mar 2026 18:48:03 +0000 (19:48 +0100)
Lib/sqlite3/__main__.py
Lib/test/test_sqlite3/test_cli.py
Misc/NEWS.d/next/Library/2025-06-24-19-07-18.gh-issue-135883.38cePA.rst [new file with mode: 0644]

index b3746ed757332f4c42670020a7a27d32c9222ee9..8805442b69e08086d7fc958df12c1a580697fb77 100644 (file)
@@ -133,8 +133,11 @@ def main(*args):
     theme = get_theme()
     s = theme.syntax
 
-    sys.ps1 = f"{s.prompt}sqlite> {s.reset}"
-    sys.ps2 = f"{s.prompt}    ... {s.reset}"
+    # Use RL_PROMPT_START_IGNORE (\001) and RL_PROMPT_END_IGNORE (\002) to
+    # bracket non-printing characters. This tells readline to ignore them
+    # when calculating screen space for redisplay during history scrolling.
+    sys.ps1 = f"\001{s.prompt}\002sqlite> \001{s.reset}\002"
+    sys.ps2 = f"\001{s.prompt}\002    ... \001{s.reset}\002"
 
     con = sqlite3.connect(args.filename, isolation_level=None)
     try:
index 98aadaa829a969a863efa0b2fbe53bbb5300e83a..1fc0236780fa8ba31c55f08cbde416d8c3f9e69e 100644 (file)
@@ -80,8 +80,8 @@ class CommandLineInterface(unittest.TestCase):
 @force_not_colorized_test_class
 class InteractiveSession(unittest.TestCase):
     MEMORY_DB_MSG = "Connected to a transient in-memory database"
-    PS1 = "sqlite> "
-    PS2 = "... "
+    PS1 = "\001\002sqlite> \001\002"
+    PS2 = "\001\002    ... \001\002"
 
     def run_cli(self, *args, commands=()):
         with (
@@ -202,8 +202,8 @@ class InteractiveSession(unittest.TestCase):
     def test_color(self):
         with unittest.mock.patch("_colorize.can_colorize", return_value=True):
             out, err = self.run_cli(commands="TEXT\n")
-            self.assertIn("\x1b[1;35msqlite> \x1b[0m", out)
-            self.assertIn("\x1b[1;35m    ... \x1b[0m\x1b", out)
+            self.assertIn("\x01\x1b[1;35m\x02sqlite> \x01\x1b[0m\x02", out)
+            self.assertIn("\x01\x1b[1;35m\x02    ... \x01\x1b[0m\x02\x01\x1b", out)
             out, err = self.run_cli(commands=("sel;",))
             self.assertIn('\x1b[1;35mOperationalError (SQLITE_ERROR)\x1b[0m: '
                           '\x1b[35mnear "sel": syntax error\x1b[0m', err)
@@ -212,6 +212,10 @@ class InteractiveSession(unittest.TestCase):
 @requires_subprocess()
 @force_not_colorized_test_class
 class Completion(unittest.TestCase):
+    # run_pty() creates a real terminal environment, where sqlite3 CLI
+    # SqliteInteractiveConsole invokes GNU Readline for input. Readline's
+    # _rl_strip_prompt() strips \001 and \002 from the output, so test
+    # assertions use the plain prompt.
     PS1 = "sqlite> "
 
     @classmethod
diff --git a/Misc/NEWS.d/next/Library/2025-06-24-19-07-18.gh-issue-135883.38cePA.rst b/Misc/NEWS.d/next/Library/2025-06-24-19-07-18.gh-issue-135883.38cePA.rst
new file mode 100644 (file)
index 0000000..8f3efce
--- /dev/null
@@ -0,0 +1,2 @@
+Fix :mod:`sqlite3`'s :ref:`interactive shell <sqlite3-cli>` keeping part of
+previous commands when scrolling history.