|  | 
| 9 | 9 | 
 | 
| 10 | 10 | from argparse import ArgumentParser | 
| 11 | 11 | from code import InteractiveConsole | 
|  | 12 | +from contextlib import contextmanager | 
| 12 | 13 | from textwrap import dedent | 
| 13 | 14 | 
 | 
| 14 | 15 | 
 | 
| @@ -62,6 +63,59 @@ def runsource(self, source, filename="<input>", symbol="single"): | 
| 62 | 63 |         return False | 
| 63 | 64 | 
 | 
| 64 | 65 | 
 | 
|  | 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 | + | 
| 65 | 119 | def main(*args): | 
| 66 | 120 |     parser = ArgumentParser( | 
| 67 | 121 |         description="Python sqlite3 CLI", | 
| @@ -114,12 +168,9 @@ def main(*args): | 
| 114 | 168 |             execute(con, args.sql, suppress_errors=False) | 
| 115 | 169 |         else: | 
| 116 | 170 |             # No SQL provided; start the REPL. | 
| 117 |  | -            console = SqliteInteractiveConsole(con) | 
| 118 |  | -            try: | 
| 119 |  | -                import readline  # noqa: F401 | 
| 120 |  | -            except ImportError: | 
| 121 |  | -                pass | 
| 122 |  | -            console.interact(banner, exitmsg="") | 
|  | 171 | +            with _enable_completer(): | 
|  | 172 | +                console = SqliteInteractiveConsole(con) | 
|  | 173 | +                console.interact(banner, exitmsg="") | 
| 123 | 174 |     finally: | 
| 124 | 175 |         con.close() | 
| 125 | 176 | 
 | 
|  | 
0 commit comments