Skip to content

Commit 391b804

Browse files
committed
Move completion code to separate module
1 parent df8c26d commit 391b804

File tree

4 files changed

+65
-56
lines changed

4 files changed

+65
-56
lines changed

Doc/whatsnew/3.14.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2176,6 +2176,9 @@ sqlite3
21762176
it will now raise a :exc:`sqlite3.ProgrammingError`.
21772177
(Contributed by Erlend E. Aasland in :gh:`118928` and :gh:`101693`.)
21782178

2179+
* Support keyword completion for :mod:`sqlite3` command-line interface.
2180+
(Contributed by Long Tan in :gh:`133393`.)
2181+
21792182
typing
21802183
------
21812184

Lib/sqlite3/__main__.py

Lines changed: 3 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99

1010
from argparse import ArgumentParser
1111
from code import InteractiveConsole
12-
from contextlib import contextmanager
1312
from textwrap import dedent
1413

14+
from ._completer import enable_completer
15+
1516

1617
def execute(c, sql, suppress_errors=True):
1718
"""Helper that wraps execution of SQL code.
@@ -63,59 +64,6 @@ def runsource(self, source, filename="<input>", symbol="single"):
6364
return False
6465

6566

66-
def _complete(text, state):
67-
keywords = ["ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ALWAYS",
68-
"ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT",
69-
"BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST",
70-
"CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT",
71-
"CONSTRAINT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE",
72-
"CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT",
73-
"DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH",
74-
"DISTINCT", "DO", "DROP", "EACH", "ELSE", "END", "ESCAPE",
75-
"EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL",
76-
"FILTER", "FIRST", "FOLLOWING", "FOR", "FOREIGN", "FROM",
77-
"FULL", "GENERATED", "GLOB", "GROUP", "GROUPS", "HAVING", "IF",
78-
"IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY",
79-
"INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS",
80-
"ISNULL", "JOIN", "KEY", "LAST", "LEFT", "LIKE", "LIMIT",
81-
"MATCH", "MATERIALIZED", "NATURAL", "NO", "NOT", "NOTHING",
82-
"NOTNULL", "NULL", "NULLS", "OF", "OFFSET", "ON", "OR",
83-
"ORDER", "OTHERS", "OUTER", "OVER", "PARTITION", "PLAN",
84-
"PRAGMA", "PRECEDING", "PRIMARY", "QUERY", "RAISE", "RANGE",
85-
"RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE",
86-
"RENAME", "REPLACE", "RESTRICT", "RETURNING", "RIGHT",
87-
"ROLLBACK", "ROW", "ROWS", "SAVEPOINT", "SELECT", "SET",
88-
"TABLE", "TEMP", "TEMPORARY", "THEN", "TIES", "TO",
89-
"TRANSACTION", "TRIGGER", "UNBOUNDED", "UNION", "UNIQUE",
90-
"UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL",
91-
"WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT"]
92-
options = [c + " " for c in keywords if c.startswith(text.upper())]
93-
try:
94-
return options[state]
95-
except IndexError:
96-
return None
97-
98-
@contextmanager
99-
def _enable_completer():
100-
try:
101-
import readline
102-
except ImportError:
103-
yield
104-
return
105-
106-
old_completer = readline.get_completer()
107-
try:
108-
readline.set_completer(_complete)
109-
if readline.backend == "editline":
110-
# libedit uses "^I" instead of "tab"
111-
command_string = "bind ^I rl_complete"
112-
else:
113-
command_string = "tab: complete"
114-
readline.parse_and_bind(command_string)
115-
yield
116-
finally:
117-
readline.set_completer(old_completer)
118-
11967
def main(*args):
12068
parser = ArgumentParser(
12169
description="Python sqlite3 CLI",
@@ -168,7 +116,7 @@ def main(*args):
168116
execute(con, args.sql, suppress_errors=False)
169117
else:
170118
# No SQL provided; start the REPL.
171-
with _enable_completer():
119+
with enable_completer():
172120
console = SqliteInteractiveConsole(con)
173121
console.interact(banner, exitmsg="")
174122
finally:

Lib/sqlite3/_completer.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env python3
2+
3+
from contextlib import contextmanager
4+
5+
6+
def _complete(text, state):
7+
keywords = ["ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ALWAYS",
8+
"ANALYZE", "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT",
9+
"BEFORE", "BEGIN", "BETWEEN", "BY", "CASCADE", "CASE", "CAST",
10+
"CHECK", "COLLATE", "COLUMN", "COMMIT", "CONFLICT",
11+
"CONSTRAINT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE",
12+
"CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT",
13+
"DEFERRABLE", "DEFERRED", "DELETE", "DESC", "DETACH",
14+
"DISTINCT", "DO", "DROP", "EACH", "ELSE", "END", "ESCAPE",
15+
"EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS", "EXPLAIN", "FAIL",
16+
"FILTER", "FIRST", "FOLLOWING", "FOR", "FOREIGN", "FROM",
17+
"FULL", "GENERATED", "GLOB", "GROUP", "GROUPS", "HAVING", "IF",
18+
"IGNORE", "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY",
19+
"INNER", "INSERT", "INSTEAD", "INTERSECT", "INTO", "IS",
20+
"ISNULL", "JOIN", "KEY", "LAST", "LEFT", "LIKE", "LIMIT",
21+
"MATCH", "MATERIALIZED", "NATURAL", "NO", "NOT", "NOTHING",
22+
"NOTNULL", "NULL", "NULLS", "OF", "OFFSET", "ON", "OR",
23+
"ORDER", "OTHERS", "OUTER", "OVER", "PARTITION", "PLAN",
24+
"PRAGMA", "PRECEDING", "PRIMARY", "QUERY", "RAISE", "RANGE",
25+
"RECURSIVE", "REFERENCES", "REGEXP", "REINDEX", "RELEASE",
26+
"RENAME", "REPLACE", "RESTRICT", "RETURNING", "RIGHT",
27+
"ROLLBACK", "ROW", "ROWS", "SAVEPOINT", "SELECT", "SET",
28+
"TABLE", "TEMP", "TEMPORARY", "THEN", "TIES", "TO",
29+
"TRANSACTION", "TRIGGER", "UNBOUNDED", "UNION", "UNIQUE",
30+
"UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL",
31+
"WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT"]
32+
options = [c + " " for c in keywords if c.startswith(text.upper())]
33+
try:
34+
return options[state]
35+
except IndexError:
36+
return None
37+
38+
39+
@contextmanager
40+
def enable_completer():
41+
try:
42+
import readline
43+
except ImportError:
44+
yield
45+
return
46+
47+
old_completer = readline.get_completer()
48+
try:
49+
readline.set_completer(_complete)
50+
if readline.backend == "editline":
51+
# libedit uses "^I" instead of "tab"
52+
command_string = "bind ^I rl_complete"
53+
else:
54+
command_string = "tab: complete"
55+
readline.parse_and_bind(command_string)
56+
yield
57+
finally:
58+
readline.set_completer(old_completer)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Support completion for :mod:`sqlite3` command-line interface.
1+
Support keyword completion for :mod:`sqlite3` command-line interface.

0 commit comments

Comments
 (0)