From 745947cda0377bb44d791b7d8e58b327a9d84844 Mon Sep 17 00:00:00 2001 From: Tan Long Date: Wed, 4 Mar 2026 02:48:03 +0800 Subject: [PATCH 1/3] gh-135883: Fix sqlite3 CLI history scrolling with colored prompts (#135884) --- Lib/sqlite3/__main__.py | 7 +++++-- Lib/test/test_sqlite3/test_cli.py | 12 ++++++++---- .../2025-06-24-19-07-18.gh-issue-135883.38cePA.rst | 2 ++ 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-06-24-19-07-18.gh-issue-135883.38cePA.rst diff --git a/Lib/sqlite3/__main__.py b/Lib/sqlite3/__main__.py index b3746ed757332f..8805442b69e080 100644 --- a/Lib/sqlite3/__main__.py +++ b/Lib/sqlite3/__main__.py @@ -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: diff --git a/Lib/test/test_sqlite3/test_cli.py b/Lib/test/test_sqlite3/test_cli.py index 98aadaa829a969..1fc0236780fa8b 100644 --- a/Lib/test/test_sqlite3/test_cli.py +++ b/Lib/test/test_sqlite3/test_cli.py @@ -80,8 +80,8 @@ def test_cli_on_disk_db(self): @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 @@ def test_interact_on_disk_file(self): 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 @@ def test_color(self): @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 index 00000000000000..8f3efceaae1d91 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-24-19-07-18.gh-issue-135883.38cePA.rst @@ -0,0 +1,2 @@ +Fix :mod:`sqlite3`'s :ref:`interactive shell ` keeping part of +previous commands when scrolling history. From 15f6479c415cc6cd219cd25c1d94bab17d720cbc Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 3 Mar 2026 15:48:43 -0500 Subject: [PATCH 2/3] Docs: use a Sphinx extension to eliminate excessive links (#145130) --- Doc/conf.py | 1 + Doc/requirements.txt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Doc/conf.py b/Doc/conf.py index d7effe2572ec44..545049bb460419 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -43,6 +43,7 @@ # Skip if downstream redistributors haven't installed them _OPTIONAL_EXTENSIONS = ( + 'linklint.ext', 'notfound.extension', 'sphinxext.opengraph', ) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index d0107744ecbe85..536ae57e4efc29 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -18,4 +18,6 @@ sphinx-notfound-page~=1.0.0 # to install that as well. python-docs-theme>=2023.3.1,!=2023.7 +linklint + -c constraints.txt From dc12d1999b88e84d5a6b8e491be468b73379e54b Mon Sep 17 00:00:00 2001 From: Justin Kunimune Date: Tue, 3 Mar 2026 16:41:26 -0500 Subject: [PATCH 3/3] Fix incorrect statement about argparse.ArgumentParser.add_argument() (#145479) Co-authored-by: Savannah Ostrowski --- Doc/library/argparse.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index ca4f439c345f32..8f31e815e0eb4b 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -739,9 +739,9 @@ By default, :mod:`!argparse` automatically handles the internal naming and display names of arguments, simplifying the process without requiring additional configuration. As such, you do not need to specify the dest_ and metavar_ parameters. -The dest_ parameter defaults to the argument name with underscores ``_`` -replacing hyphens ``-`` . The metavar_ parameter defaults to the -upper-cased name. For example:: +For optional arguments, the dest_ parameter defaults to the argument name, with +underscores ``_`` replacing hyphens ``-``. The metavar_ parameter defaults to +the upper-cased name. For example:: >>> parser = argparse.ArgumentParser(prog='PROG') >>> parser.add_argument('--foo-bar')