mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-29 03:04:38 +08:00
Fix sqlite Connection initialization check (#6199)
* Fix sqlite3 Connection initialization check Add proper __init__ validation for sqlite3.Connection to ensure base class __init__ is called before using connection methods. This fixes the test_connection_constructor_call_check test case. Changes: - Modified Connection.py_new to detect subclassing - For base Connection class, initialization happens immediately in py_new - For subclassed Connection, db is initialized as None - Added __init__ method that performs actual database initialization - Updated _db_lock error message to match CPython: 'Base Connection.__init__ not called.' This ensures CPython compatibility where attempting to use a Connection subclass instance without calling the base __init__ raises ProgrammingError. * use Initializer trait
This commit is contained in:
1 parent
25a464eeae
commit
2faa05dcfb
2 files changed
+47
-19
No files matched your search
-2
@@ -219,8 +219,6 @@ class RegressionTests(unittest.TestCase):
|
||||
class MyStr(str): pass
|
||||
self.con.execute("select ?", (MyStr("abc"),))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_connection_constructor_call_check(self):
|
||||
"""
|
||||
Verifies that connection methods check whether base class __init__ was
|
||||
|
||||
+47
-17
@@ -74,8 +74,8 @@ mod _sqlite {
|
||||
},
|
||||
sliceable::{SaturatedSliceIter, SliceableSequenceOp},
|
||||
types::{
|
||||
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable, IterNext,
|
||||
Iterable, PyComparisonOp, SelfIter, Unconstructible,
|
||||
AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable,
|
||||
Initializer, IterNext, Iterable, PyComparisonOp, SelfIter, Unconstructible,
|
||||
},
|
||||
utils::ToCString,
|
||||
};
|
||||
@@ -851,7 +851,31 @@ mod _sqlite {
|
||||
type Args = ConnectArgs;
|
||||
|
||||
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
|
||||
Ok(Self::new(args, vm)?.into_ref_with_type(vm, cls)?.into())
|
||||
let text_factory = PyStr::class(&vm.ctx).to_owned().into_object();
|
||||
|
||||
// For non-subclassed Connection, initialize in __new__
|
||||
// For subclassed Connection, leave db as None and require __init__ to be called
|
||||
let is_base_class = cls.is(Connection::class(&vm.ctx).as_object());
|
||||
|
||||
let db = if is_base_class {
|
||||
// Initialize immediately for base class
|
||||
Some(Connection::initialize_db(&args, vm)?)
|
||||
} else {
|
||||
// For subclasses, require __init__ to be called
|
||||
None
|
||||
};
|
||||
|
||||
let conn = Self {
|
||||
db: PyMutex::new(db),
|
||||
detect_types: args.detect_types,
|
||||
isolation_level: PyAtomicRef::from(args.isolation_level),
|
||||
check_same_thread: args.check_same_thread,
|
||||
thread_ident: std::thread::current().id(),
|
||||
row_factory: PyAtomicRef::from(None),
|
||||
text_factory: PyAtomicRef::from(text_factory),
|
||||
};
|
||||
|
||||
Ok(conn.into_ref_with_type(vm, cls)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,9 +895,25 @@ mod _sqlite {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(with(Constructor, Callable), flags(BASETYPE))]
|
||||
impl Initializer for Connection {
|
||||
type Args = ConnectArgs;
|
||||
|
||||
fn init(zelf: PyRef<Self>, args: Self::Args, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let mut guard = zelf.db.lock();
|
||||
if guard.is_some() {
|
||||
// Already initialized
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let db = Self::initialize_db(&args, vm)?;
|
||||
*guard = Some(db);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(with(Constructor, Callable, Initializer), flags(BASETYPE))]
|
||||
impl Connection {
|
||||
fn new(args: ConnectArgs, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
fn initialize_db(args: &ConnectArgs, vm: &VirtualMachine) -> PyResult<Sqlite> {
|
||||
let path = args.database.to_cstring(vm)?;
|
||||
let db = Sqlite::from(SqliteRaw::open(path.as_ptr(), args.uri, vm)?);
|
||||
let timeout = (args.timeout * 1000.0) as c_int;
|
||||
@@ -881,17 +921,7 @@ mod _sqlite {
|
||||
if let Some(isolation_level) = &args.isolation_level {
|
||||
begin_statement_ptr_from_isolation_level(isolation_level, vm)?;
|
||||
}
|
||||
let text_factory = PyStr::class(&vm.ctx).to_owned().into_object();
|
||||
|
||||
Ok(Self {
|
||||
db: PyMutex::new(Some(db)),
|
||||
detect_types: args.detect_types,
|
||||
isolation_level: PyAtomicRef::from(args.isolation_level),
|
||||
check_same_thread: args.check_same_thread,
|
||||
thread_ident: std::thread::current().id(),
|
||||
row_factory: PyAtomicRef::from(None),
|
||||
text_factory: PyAtomicRef::from(text_factory),
|
||||
})
|
||||
Ok(db)
|
||||
}
|
||||
|
||||
fn db_lock(&self, vm: &VirtualMachine) -> PyResult<PyMappedMutexGuard<'_, Sqlite>> {
|
||||
@@ -908,7 +938,7 @@ mod _sqlite {
|
||||
} else {
|
||||
Err(new_programming_error(
|
||||
vm,
|
||||
"Cannot operate on a closed database.".to_owned(),
|
||||
"Base Connection.__init__ not called.".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user