Skip to content

Commit 1d623e5

Browse files
committed
Fix sqlite3 Cursor initialization check
Add proper __init__ validation for sqlite3.Cursor to ensure base class __init__ is called before using cursor methods. This fixes the test_cursor_constructor_call_check test case. Changes: - Modified Cursor to initialize with inner=None in py_new - Added explicit __init__ method that sets up CursorInner - Updated close() method to check for uninitialized state - Changed error message to match CPython: 'Base Cursor.__init__ not called.' This ensures CPython compatibility where attempting to use a Cursor instance without calling the base __init__ raises ProgrammingError.
1 parent 3b48dcc commit 1d623e5

File tree

2 files changed

+40
-6
lines changed

2 files changed

+40
-6
lines changed

Lib/test/test_sqlite3/test_regression.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,6 @@ def __del__(self):
195195
con.isolation_level = value
196196
self.assertEqual(con.isolation_level, "DEFERRED")
197197

198-
# TODO: RUSTPYTHON
199-
@unittest.expectedFailure
200198
def test_cursor_constructor_call_check(self):
201199
"""
202200
Verifies that cursor methods check whether base class __init__ was

stdlib/src/sqlite.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,32 @@ mod _sqlite {
14881488
}
14891489
}
14901490

1491+
fn new_uninitialized(connection: PyRef<Connection>, _vm: &VirtualMachine) -> Self {
1492+
Self {
1493+
connection,
1494+
arraysize: Radium::new(1),
1495+
row_factory: PyAtomicRef::from(None),
1496+
inner: PyMutex::from(None),
1497+
}
1498+
}
1499+
1500+
#[pymethod]
1501+
fn __init__(&self, _connection: PyRef<Connection>, _vm: &VirtualMachine) -> PyResult<()> {
1502+
let mut guard = self.inner.lock();
1503+
if guard.is_some() {
1504+
// Already initialized (e.g., from a call to super().__init__)
1505+
return Ok(());
1506+
}
1507+
*guard = Some(CursorInner {
1508+
description: None,
1509+
row_cast_map: vec![],
1510+
lastrowid: -1,
1511+
rowcount: -1,
1512+
statement: None,
1513+
});
1514+
Ok(())
1515+
}
1516+
14911517
fn inner(&self, vm: &VirtualMachine) -> PyResult<PyMappedMutexGuard<'_, CursorInner>> {
14921518
let guard = self.inner.lock();
14931519
if guard.is_some() {
@@ -1497,7 +1523,7 @@ mod _sqlite {
14971523
} else {
14981524
Err(new_programming_error(
14991525
vm,
1500-
"Cannot operate on a closed cursor.".to_owned(),
1526+
"Base Cursor.__init__ not called.".to_owned(),
15011527
))
15021528
}
15031529
}
@@ -1717,12 +1743,22 @@ mod _sqlite {
17171743
}
17181744

17191745
#[pymethod]
1720-
fn close(&self) {
1721-
if let Some(inner) = self.inner.lock().take()
1746+
fn close(&self, vm: &VirtualMachine) -> PyResult<()> {
1747+
// Check if __init__ was called
1748+
let mut guard = self.inner.lock();
1749+
if guard.is_none() {
1750+
return Err(new_programming_error(
1751+
vm,
1752+
"Base Cursor.__init__ not called.".to_owned(),
1753+
));
1754+
}
1755+
1756+
if let Some(inner) = guard.take()
17221757
&& let Some(stmt) = inner.statement
17231758
{
17241759
stmt.lock().reset();
17251760
}
1761+
Ok(())
17261762
}
17271763

17281764
#[pymethod]
@@ -1809,7 +1845,7 @@ mod _sqlite {
18091845
type Args = (PyRef<Connection>,);
18101846

18111847
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
1812-
Self::new(args.0, None, vm)
1848+
Self::new_uninitialized(args.0, vm)
18131849
.into_ref_with_type(vm, cls)
18141850
.map(Into::into)
18151851
}

0 commit comments

Comments
 (0)