diff options
author | Erlend Egeberg Aasland <erlend.aasland@protonmail.com> | 2022-08-01 10:25:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-01 10:25:16 (GMT) |
commit | bc7c7cd18a4ae015449f95454a762a7276585bb8 (patch) | |
tree | 2b477534f2442eb0af34526bbdc5fa9217bfe9f1 /Lib/sqlite3 | |
parent | 1e6b63542e4856436c5c12148a6608ef9d148b71 (diff) | |
download | cpython-bc7c7cd18a4ae015449f95454a762a7276585bb8.zip cpython-bc7c7cd18a4ae015449f95454a762a7276585bb8.tar.gz cpython-bc7c7cd18a4ae015449f95454a762a7276585bb8.tar.bz2 |
gh-77617: Add sqlite3 command-line interface (#95026)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
Diffstat (limited to 'Lib/sqlite3')
-rw-r--r-- | Lib/sqlite3/__main__.py | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/Lib/sqlite3/__main__.py b/Lib/sqlite3/__main__.py new file mode 100644 index 0000000..c62fad8 --- /dev/null +++ b/Lib/sqlite3/__main__.py @@ -0,0 +1,97 @@ +import sqlite3 +import sys + +from argparse import ArgumentParser +from code import InteractiveConsole +from textwrap import dedent + + +def execute(c, sql, suppress_errors=True): + try: + for row in c.execute(sql): + print(row) + except sqlite3.Error as e: + tp = type(e).__name__ + try: + print(f"{tp} ({e.sqlite_errorname}): {e}", file=sys.stderr) + except AttributeError: + print(f"{tp}: {e}", file=sys.stderr) + if not suppress_errors: + sys.exit(1) + + +class SqliteInteractiveConsole(InteractiveConsole): + + def __init__(self, connection): + super().__init__() + self._con = connection + self._cur = connection.cursor() + + def runsource(self, source, filename="<input>", symbol="single"): + match source: + case ".version": + print(f"{sqlite3.sqlite_version}") + case ".help": + print("Enter SQL code and press enter.") + case ".quit": + sys.exit(0) + case _: + if not sqlite3.complete_statement(source): + return True + execute(self._cur, source) + return False + + +def main(): + parser = ArgumentParser( + description="Python sqlite3 CLI", + prog="python -m sqlite3", + ) + parser.add_argument( + "filename", type=str, default=":memory:", nargs="?", + help=( + "SQLite database to open (defaults to ':memory:'). " + "A new database is created if the file does not previously exist." + ), + ) + parser.add_argument( + "sql", type=str, nargs="?", + help=( + "An SQL query to execute. " + "Any returned rows are printed to stdout." + ), + ) + parser.add_argument( + "-v", "--version", action="version", + version=f"SQLite version {sqlite3.sqlite_version}", + help="Print underlying SQLite library version", + ) + args = parser.parse_args() + + if args.filename == ":memory:": + db_name = "a transient in-memory database" + else: + db_name = repr(args.filename) + + banner = dedent(f""" + sqlite3 shell, running on SQLite version {sqlite3.sqlite_version} + Connected to {db_name} + + Each command will be run using execute() on the cursor. + Type ".help" for more information; type ".quit" or CTRL-D to quit. + """).strip() + sys.ps1 = "sqlite> " + sys.ps2 = " ... " + + con = sqlite3.connect(args.filename, isolation_level=None) + try: + if args.sql: + execute(con, args.sql, suppress_errors=False) + else: + console = SqliteInteractiveConsole(con) + console.interact(banner, exitmsg="") + finally: + con.close() + + +main() |