From afd2b16a4cf856b050f382285e627f7bf070a9d8 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Sat, 15 Aug 2026 15:13:07 +0900 Subject: [PATCH] sqlite3: add Connection exception attributes and fix autocommit ValueError Add DB-API 2.0 optional extension: expose exception classes as attributes on Connection objects (Warning, Error, InterfaceError, DatabaseError, DataError, OperationalError, IntegrityError, InternalError, ProgrammingError, NotSupportedError). Also fix autocommit validation to raise ValueError (not TypeError) when an unsupported type is passed. Assisted-by: GitHub Copilot:claude-sonnet-4-6 --- Lib/test/test_sqlite3/test_dbapi.py | 2 - Lib/test/test_sqlite3/test_transactions.py | 1 - crates/stdlib/src/_sqlite3.rs | 43 +++++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 68f8969a00b..f99825f63d8 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -364,7 +364,6 @@ def test_use_after_close(self): with self.cx: pass - @unittest.expectedFailure # TODO: RUSTPYTHON def test_exceptions(self): # Optional DB-API extension. self.assertEqual(self.cx.Warning, sqlite.Warning) @@ -401,7 +400,6 @@ def test_in_transaction_ro(self): with self.assertRaises(AttributeError): self.cx.in_transaction = True - @unittest.expectedFailure # TODO: RUSTPYTHON def test_connection_exceptions(self): exceptions = [ "DataError", diff --git a/Lib/test/test_sqlite3/test_transactions.py b/Lib/test/test_sqlite3/test_transactions.py index d777af0ffe6..a3de7a7a82e 100644 --- a/Lib/test/test_sqlite3/test_transactions.py +++ b/Lib/test/test_sqlite3/test_transactions.py @@ -387,7 +387,6 @@ def test_autocommit_setget(self): cx.autocommit = mode self.assertEqual(cx.autocommit, mode) - @unittest.expectedFailure # TODO: RUSTPYTHON; autocommit validation error messages differ def test_autocommit_setget_invalid(self): msg = "autocommit must be True, False, or.*LEGACY" for mode in "a", 12, (), None: diff --git a/crates/stdlib/src/_sqlite3.rs b/crates/stdlib/src/_sqlite3.rs index 5348cc1f5ec..e04bc4a69f8 100644 --- a/crates/stdlib/src/_sqlite3.rs +++ b/crates/stdlib/src/_sqlite3.rs @@ -322,7 +322,7 @@ mod _sqlite3 { ))) } } else { - Err(vm.new_type_error(format!( + Err(vm.new_value_error(format!( "autocommit must be True, False, or sqlite3.LEGACY_TRANSACTION_CONTROL, not {}", obj.class().name() ))) @@ -1642,6 +1642,47 @@ mod _sqlite3 { fn total_changes(&self, vm: &VirtualMachine) -> PyResult { self._db_lock(vm).map(|x| x.total_changes()) } + + #[pygetset(name = "Warning")] + fn exc_warning(&self) -> PyTypeRef { + warning_type().to_owned() + } + #[pygetset(name = "Error")] + fn exc_error(&self) -> PyTypeRef { + error_type().to_owned() + } + #[pygetset(name = "InterfaceError")] + fn exc_interface_error(&self) -> PyTypeRef { + interface_error_type().to_owned() + } + #[pygetset(name = "DatabaseError")] + fn exc_database_error(&self) -> PyTypeRef { + database_error_type().to_owned() + } + #[pygetset(name = "DataError")] + fn exc_data_error(&self) -> PyTypeRef { + data_error_type().to_owned() + } + #[pygetset(name = "OperationalError")] + fn exc_operational_error(&self) -> PyTypeRef { + operational_error_type().to_owned() + } + #[pygetset(name = "IntegrityError")] + fn exc_integrity_error(&self) -> PyTypeRef { + integrity_error_type().to_owned() + } + #[pygetset(name = "InternalError")] + fn exc_internal_error(&self) -> PyTypeRef { + internal_error_type().to_owned() + } + #[pygetset(name = "ProgrammingError")] + fn exc_programming_error(&self) -> PyTypeRef { + programming_error_type().to_owned() + } + #[pygetset(name = "NotSupportedError")] + fn exc_not_supported_error(&self) -> PyTypeRef { + not_supported_error_type().to_owned() + } } #[pyattr]