Skip to content
Merged
30 changes: 19 additions & 11 deletions Lib/sqlite3/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,25 @@ def runsource(self, source, filename="<input>", symbol="single"):
Return True if more input is needed; buffering is done automatically.
Return False if input is a complete statement ready for execution.
"""
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)
if not source or source.isspace():
return False
if source[0] == ".":
match source[1:].strip():
case "version":
print(f"{sqlite3.sqlite_version}")
case "help":
print("Enter SQL code and press enter.")
case "quit":
sys.exit(0)
case "":
pass
case _ as unknown:
self.write("Error: unknown command or invalid arguments:"
f' "{unknown}".\n')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error messages do not end with a '.' e.g.:

Python 3.15.0a0 (heads/main:c81fa2b9cd1, May  8 2025, 16:38:25) [GCC 15.1.1 20250425 (Red Hat 15.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> test
Traceback (most recent call last):
  File "<python-input-0>", line 1, in <module>
    test
NameError: name 'test' is not defined

else:
if not sqlite3.complete_statement(source):
return True
execute(self._cur, source)
return False


Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_sqlite3/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,38 @@ def test_interact_version(self):
self.assertEqual(out.count(self.PS2), 0)
self.assertIn(sqlite3.sqlite_version, out)

def test_interact_empty_source(self):
out, err = self.run_cli(commands=("", " "))
self.assertIn(self.MEMORY_DB_MSG, err)
self.assertEndsWith(out, self.PS1)
self.assertEqual(out.count(self.PS1), 3)
self.assertEqual(out.count(self.PS2), 0)

def test_interact_dot_commands_unknown(self):
out, err = self.run_cli(commands=(".unknown_command", ))
self.assertIn(self.MEMORY_DB_MSG, err)
self.assertEndsWith(out, self.PS1)
self.assertEqual(out.count(self.PS1), 2)
self.assertEqual(out.count(self.PS2), 0)
self.assertIn("Error", err)
# test "unknown_command" is pointed out in the error message
self.assertIn("unknown_command", err)

def test_interact_dot_commands_empty(self):
out, err = self.run_cli(commands=("."))
self.assertIn(self.MEMORY_DB_MSG, err)
self.assertEndsWith(out, self.PS1)
self.assertEqual(out.count(self.PS1), 2)
self.assertEqual(out.count(self.PS2), 0)

def test_interact_dot_commands_with_whitespaces(self):
out, err = self.run_cli(commands=(".version ", ". version"))
self.assertIn(self.MEMORY_DB_MSG, err)
self.assertEqual(out.count(sqlite3.sqlite_version + "\n"), 2)
self.assertEndsWith(out, self.PS1)
self.assertEqual(out.count(self.PS1), 3)
self.assertEqual(out.count(self.PS2), 0)

def test_interact_valid_sql(self):
out, err = self.run_cli(commands=("SELECT 1;",))
self.assertIn(self.MEMORY_DB_MSG, err)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix dot commands with trailing spaces are mistaken for multi-line SQL
statements in the sqlite3 command-line interface.
Loading