Skip to content

Commit 251b17c

Browse files
authored
Merge branch 'main' into sqlite3_file_control
2 parents e7bc25d + ef1fce0 commit 251b17c

7 files changed

Lines changed: 53 additions & 8 deletions

File tree

‎Lib/_pyrepl/utils.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,21 @@
1919
from .trace import trace
2020

2121

22-
ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]")
22+
ANSI_ESCAPE_SEQUENCE = re.compile(
23+
r"""
24+
\x1b
25+
(?:
26+
# CSI: ESC [ followed by parameter/intermediate bytes and a final byte.
27+
# For example, ESC [ 31 m ("\x1b[31m") selects red foreground text.
28+
\[ [ -@]* [A-~]
29+
|
30+
# OSC: ESC ] followed by a payload terminated by BEL or ST (ESC \).
31+
# For example, ESC ] 0 ; title BEL ("\x1b]0;title\x07") sets the title.
32+
\] [^\x1b\x07]* (?: \x07 | \x1b\\ )
33+
)
34+
""",
35+
re.X,
36+
)
2337
ZERO_WIDTH_BRACKET = re.compile(r"\x01.*?\x02")
2438
ZERO_WIDTH_TRANS = str.maketrans({"\x01": "", "\x02": ""})
2539
IDENTIFIERS_AFTER = frozenset({"def", "class"})

‎Lib/sqlite3/__main__.py‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
from ._completer import completer
1616

1717

18+
# IDLE uses CTRL-D for EOF on all platforms (gh-102628).
19+
if sys.platform == "win32" and "idlelib.run" not in sys.modules:
20+
EOF_KEY = "CTRL-Z"
21+
else:
22+
EOF_KEY = "CTRL-D"
23+
24+
1825
def execute(c, sql, suppress_errors=True, theme=theme_no_color):
1926
"""Helper that wraps execution of SQL code.
2027
@@ -70,7 +77,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
7077
print(f"Enter SQL code or one of the below commands, and press enter.\n\n"
7178
f"{t.builtin}.version{t.reset} Print underlying SQLite library version\n"
7279
f"{t.builtin}.help{t.reset} Print this help message\n"
73-
f"{t.builtin}.quit{t.reset} Exit the CLI, equivalent to CTRL-D\n")
80+
f"{t.builtin}.quit{t.reset} Exit the CLI, equivalent to {EOF_KEY}\n")
7481
case "quit":
7582
sys.exit(0)
7683
case "":
@@ -115,16 +122,12 @@ def main(*args):
115122
db_name = repr(args.filename)
116123

117124
# Prepare REPL banner and prompts.
118-
if sys.platform == "win32" and "idlelib.run" not in sys.modules:
119-
eofkey = "CTRL-Z"
120-
else:
121-
eofkey = "CTRL-D"
122125
banner = dedent(f"""
123126
sqlite3 shell, running on SQLite version {sqlite3.sqlite_version}
124127
Connected to {db_name}
125128
126129
Each command will be run using execute() on the cursor.
127-
Type ".help" for more information; type ".quit" or {eofkey} to quit.
130+
Type ".help" for more information; type ".quit" or {EOF_KEY} to quit.
128131
""").strip()
129132

130133
theme = get_theme()

‎Lib/test/test_pyrepl/test_render.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ def test_from_rendered_text_empty_string(self):
5555
self.assertEqual(line.text, "")
5656
self.assertEqual(line.width, 0)
5757

58+
def test_from_rendered_text_with_osc_control(self):
59+
osc = "\x1b]633;A\x1b\\"
60+
prompt = ">>> "
61+
line = RenderLine.from_rendered_text(osc + prompt)
62+
63+
self.assertEqual(line.width, len(prompt))
64+
self.assertEqual(line.cells[0].controls, (osc,))
65+
self.assertEqual(line.cells[0].text, "")
66+
5867
def test_from_rendered_text_with_non_sgr_controls(self):
5968
# \x1b[H is a cursor-home control (not SGR since it doesn't end with 'm')
6069
line = RenderLine.from_rendered_text("\x1b[Hx")

‎Lib/test/test_pyrepl/test_utils.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ def test_wlen(self):
4747
self.assertEqual(wlen('e\N{COMBINING ACUTE ACCENT}'), 1)
4848
self.assertEqual(wlen('a\N{ZERO WIDTH JOINER}b'), 2)
4949

50+
def test_wlen_with_osc_sequence(self):
51+
prompt = ">>> "
52+
self.assertEqual(wlen("\x1b]633;A\x07" + prompt), len(prompt))
53+
54+
def test_wlen_with_unterminated_osc_sequence_as_individual_characters(self):
55+
# \x1b is treated as a single character (ESC)
56+
self.assertEqual(wlen("\x1b]633;A"), len("\x1b]633;A"))
57+
5058
def test_prev_next_window(self):
5159
def gen_normal():
5260
yield 1

‎Lib/test/test_sqlite3/test_cli.py‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class InteractiveSession(unittest.TestCase):
8282
MEMORY_DB_MSG = "Connected to a transient in-memory database"
8383
PS1 = "\001\002sqlite> \001\002"
8484
PS2 = "\001\002 ... \001\002"
85+
EOF_KEY = "CTRL-Z" if sys.platform == "win32" else "CTRL-D"
8586

8687
def run_cli(self, *args, commands=()):
8788
with (
@@ -104,11 +105,19 @@ def run_cli(self, *args, commands=()):
104105
def test_interact(self):
105106
out, err = self.run_cli()
106107
self.assertIn(self.MEMORY_DB_MSG, err)
107-
self.assertIn(self.MEMORY_DB_MSG, err)
108+
self.assertIn(f'type ".quit" or {self.EOF_KEY} to quit', err)
108109
self.assertEndsWith(out, self.PS1)
109110
self.assertEqual(out.count(self.PS1), 1)
110111
self.assertEqual(out.count(self.PS2), 0)
111112

113+
def test_interact_help(self):
114+
out, err = self.run_cli(commands=(".help",))
115+
self.assertIn(self.MEMORY_DB_MSG, err)
116+
self.assertIn(f"Exit the CLI, equivalent to {self.EOF_KEY}", out)
117+
self.assertEndsWith(out, self.PS1)
118+
self.assertEqual(out.count(self.PS1), 2)
119+
self.assertEqual(out.count(self.PS2), 0)
120+
112121
def test_interact_quit(self):
113122
out, err = self.run_cli(commands=(".quit",))
114123
self.assertIn(self.MEMORY_DB_MSG, err)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correct the EOF key shown in the :mod:`sqlite3` CLI ``.help`` text on Windows.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Correctly detect and handle OSC sequences in PyREPL.

0 commit comments

Comments
 (0)