Skip to content

Commit ddc3666

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 ddc3666

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-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: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,39 @@ mod _sqlite {
14881488
}
14891489
}
14901490

1491+
fn new_uninitialized(
1492+
connection: PyRef<Connection>,
1493+
_vm: &VirtualMachine,
1494+
) -> Self {
1495+
Self {
1496+
connection,
1497+
arraysize: Radium::new(1),
1498+
row_factory: PyAtomicRef::from(None),
1499+
inner: PyMutex::from(None),
1500+
}
1501+
}
1502+
1503+
#[pymethod]
1504+
fn __init__(
1505+
&self,
1506+
_connection: PyRef<Connection>,
1507+
_vm: &VirtualMachine,
1508+
) -> PyResult<()> {
1509+
let mut guard = self.inner.lock();
1510+
if guard.is_some() {
1511+
// Already initialized (e.g., from a call to super().__init__)
1512+
return Ok(());
1513+
}
1514+
*guard = Some(CursorInner {
1515+
description: None,
1516+
row_cast_map: vec![],
1517+
lastrowid: -1,
1518+
rowcount: -1,
1519+
statement: None,
1520+
});
1521+
Ok(())
1522+
}
1523+
14911524
fn inner(&self, vm: &VirtualMachine) -> PyResult<PyMappedMutexGuard<'_, CursorInner>> {
14921525
let guard = self.inner.lock();
14931526
if guard.is_some() {
@@ -1497,7 +1530,7 @@ mod _sqlite {
14971530
} else {
14981531
Err(new_programming_error(
14991532
vm,
1500-
"Cannot operate on a closed cursor.".to_owned(),
1533+
"Base Cursor.__init__ not called.".to_owned(),
15011534
))
15021535
}
15031536
}
@@ -1717,12 +1750,22 @@ mod _sqlite {
17171750
}
17181751

17191752
#[pymethod]
1720-
fn close(&self) {
1721-
if let Some(inner) = self.inner.lock().take()
1753+
fn close(&self, vm: &VirtualMachine) -> PyResult<()> {
1754+
// Check if __init__ was called
1755+
let mut guard = self.inner.lock();
1756+
if guard.is_none() {
1757+
return Err(new_programming_error(
1758+
vm,
1759+
"Base Cursor.__init__ not called.".to_owned(),
1760+
));
1761+
}
1762+
1763+
if let Some(inner) = guard.take()
17221764
&& let Some(stmt) = inner.statement
17231765
{
17241766
stmt.lock().reset();
17251767
}
1768+
Ok(())
17261769
}
17271770

17281771
#[pymethod]
@@ -1809,7 +1852,7 @@ mod _sqlite {
18091852
type Args = (PyRef<Connection>,);
18101853

18111854
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
1812-
Self::new(args.0, None, vm)
1855+
Self::new_uninitialized(args.0, vm)
18131856
.into_ref_with_type(vm, cls)
18141857
.map(Into::into)
18151858
}

0 commit comments

Comments
 (0)