From bf872a99edb00089c0eb725ce0c268e551f84da0 Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Sun, 18 Jan 2026 22:51:32 +0900 Subject: [PATCH] sqlite3: fix Connection.cursor() factory argument handling Fix test_is_instance in CursorFactoryTests by properly handling the factory argument in Connection.cursor() method. Now the factory can be passed as both positional and keyword argument, and returns the correct subclass type instead of always returning PyRef. - Use FromArgs derive macro with CursorArgs struct for argument parsing - Return PyObjectRef instead of PyRef to allow subclasses - Use fast_issubclass to validate returned cursor is a Cursor subclass - Properly differentiate between 'no argument' and 'None passed' --- Lib/test/test_sqlite3/test_factory.py | 2 -- crates/stdlib/src/sqlite.rs | 37 ++++++++++++++++++--------- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py index c13a7481520..20cff8b585f 100644 --- a/Lib/test/test_sqlite3/test_factory.py +++ b/Lib/test/test_sqlite3/test_factory.py @@ -80,8 +80,6 @@ def setUp(self): def tearDown(self): self.con.close() - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_is_instance(self): cur = self.con.cursor() self.assertIsInstance(cur, sqlite.Cursor) diff --git a/crates/stdlib/src/sqlite.rs b/crates/stdlib/src/sqlite.rs index 7e0392b1f30..9b7b810b25c 100644 --- a/crates/stdlib/src/sqlite.rs +++ b/crates/stdlib/src/sqlite.rs @@ -425,6 +425,12 @@ mod _sqlite { name: PyStrRef, } + #[derive(FromArgs)] + struct CursorArgs { + #[pyarg(any, default)] + factory: OptionalArg, + } + struct CallbackData { obj: NonNull, vm: *const VirtualMachine, @@ -1023,22 +1029,29 @@ mod _sqlite { #[pymethod] fn cursor( zelf: PyRef, - factory: OptionalArg, + args: CursorArgs, vm: &VirtualMachine, - ) -> PyResult> { + ) -> PyResult { zelf.db_lock(vm).map(drop)?; - let cursor = if let OptionalArg::Present(factory) = factory { - let cursor = factory.invoke((zelf.clone(),), vm)?; - let cursor = cursor.downcast::().map_err(|x| { - vm.new_type_error(format!("factory must return a cursor, not {}", x.class())) - })?; - let _ = unsafe { cursor.row_factory.swap(zelf.row_factory.to_owned()) }; - cursor - } else { - let row_factory = zelf.row_factory.to_owned(); - Cursor::new(zelf, row_factory, vm).into_ref(&vm.ctx) + let factory = match args.factory { + OptionalArg::Present(f) => f, + OptionalArg::Missing => Cursor::class(&vm.ctx).to_owned().into(), }; + + let cursor = factory.call((zelf.clone(),), vm)?; + + if !cursor.class().fast_issubclass(Cursor::class(&vm.ctx)) { + return Err(vm.new_type_error(format!( + "factory must return a cursor, not {}", + cursor.class() + ))); + } + + if let Some(cursor_ref) = cursor.downcast_ref::() { + let _ = unsafe { cursor_ref.row_factory.swap(zelf.row_factory.to_owned()) }; + } + Ok(cursor) }