Skip to content

Commit c1941cb

Browse files
committed
Move completion code to separate module
1 parent 5e50871 commit c1941cb

4 files changed

Lines changed: 65 additions & 56 deletions

File tree

Doc/whatsnew/3.14.rst

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

2390+
* Support keyword completion for :mod:`sqlite3` command-line interface.
2391+
(Contributed by Long Tan in :gh:`133393`.)
2392+
23902393
typing
23912394
------
23922395

Lib/sqlite3/__main__.py

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

1010
from argparse import ArgumentParser
1111
from code import InteractiveConsole
12-
from contextlib import contextmanager
1312
from textwrap import dedent
1413
from _colorize import get_theme, theme_no_color
1514

15+
from ._completer import enable_completer
16+
1617

1718
def execute(c, sql, suppress_errors=True, theme=theme_no_color):
1819
"""Helper that wraps execution of SQL code.
@@ -80,59 +81,6 @@ def runsource(self, source, filename="<input>", symbol="single"):
8081
return False
8182

8283

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