From a2b194a6f8fe2dbf2378ba9869cf1f61dce3e57e Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Mon, 1 Sep 2025 15:23:04 +0900 Subject: [PATCH 1/2] sqlite3: Support 'size' keyword argument in `Cursor::fetchmany` --- Lib/test/test_sqlite3/test_dbapi.py | 2 -- stdlib/src/sqlite.rs | 32 ++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 65821020a61..49c9764a1b7 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -1069,8 +1069,6 @@ def test_fetchmany(self): res = self.cu.fetchmany(100) self.assertEqual(res, []) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_fetchmany_kw_arg(self): """Checks if fetchmany works with keyword arguments""" self.cu.execute("select name from test") diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index 8bba155612b..de07c2ef8b2 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -1684,14 +1684,40 @@ mod _sqlite { #[pymethod] fn fetchmany( zelf: &Py, - max_rows: OptionalArg, + mut args: FuncArgs, vm: &VirtualMachine, ) -> PyResult> { - let max_rows = max_rows.unwrap_or_else(|| zelf.arraysize.load(Ordering::Relaxed)); + let size_posarg = args.take_positional(); + let size_kwarg = args.take_keyword("size"); + + if !args.args.is_empty() { + return Err(vm.new_type_error(format!( + "fetchmany() takes from 0 to 1 positional arguments but {} were given", + args.args.len() + 1 + ))); + } + + if let Some((name_str, _)) = args.kwargs.into_iter().next() { + return Err(vm.new_type_error(format!( + "fetchmany() got an unexpected keyword argument {name_str}", + ))); + } + + let max_rows: c_int = match (size_posarg, size_kwarg) { + (Some(pos), None) => pos.try_into_value(vm)?, + (None, Some(kw)) => kw.try_into_value(vm)?, + (None, None) => zelf.arraysize.load(Ordering::Relaxed), + (Some(_), Some(_)) => { + return Err(vm.new_type_error( + "fetchmany() got multiple values for argument 'size'".to_owned(), + )); + } + }; + let mut list = vec![]; while let PyIterReturn::Return(row) = Self::next(zelf, vm)? { list.push(row); - if list.len() as c_int >= max_rows { + if max_rows > 0 && list.len() as c_int >= max_rows { break; } } From 2e16f51c68dde8d8c0f79d5745b874ea043f762e Mon Sep 17 00:00:00 2001 From: Jiseok CHOI Date: Mon, 1 Sep 2025 15:43:07 +0900 Subject: [PATCH 2/2] use `FromArgs` --- stdlib/src/sqlite.rs | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/stdlib/src/sqlite.rs b/stdlib/src/sqlite.rs index de07c2ef8b2..15d3c80894b 100644 --- a/stdlib/src/sqlite.rs +++ b/stdlib/src/sqlite.rs @@ -1461,6 +1461,12 @@ mod _sqlite { statement: Option>, } + #[derive(FromArgs)] + struct FetchManyArgs { + #[pyarg(any, name = "size", optional)] + size: Option, + } + #[pyclass(with(Constructor, IterNext, Iterable), flags(BASETYPE))] impl Cursor { fn new( @@ -1684,38 +1690,15 @@ mod _sqlite { #[pymethod] fn fetchmany( zelf: &Py, - mut args: FuncArgs, + args: FetchManyArgs, vm: &VirtualMachine, ) -> PyResult> { - let size_posarg = args.take_positional(); - let size_kwarg = args.take_keyword("size"); - - if !args.args.is_empty() { - return Err(vm.new_type_error(format!( - "fetchmany() takes from 0 to 1 positional arguments but {} were given", - args.args.len() + 1 - ))); - } - - if let Some((name_str, _)) = args.kwargs.into_iter().next() { - return Err(vm.new_type_error(format!( - "fetchmany() got an unexpected keyword argument {name_str}", - ))); - } - - let max_rows: c_int = match (size_posarg, size_kwarg) { - (Some(pos), None) => pos.try_into_value(vm)?, - (None, Some(kw)) => kw.try_into_value(vm)?, - (None, None) => zelf.arraysize.load(Ordering::Relaxed), - (Some(_), Some(_)) => { - return Err(vm.new_type_error( - "fetchmany() got multiple values for argument 'size'".to_owned(), - )); - } - }; + let max_rows = args + .size + .unwrap_or_else(|| zelf.arraysize.load(Ordering::Relaxed)); let mut list = vec![]; - while let PyIterReturn::Return(row) = Self::next(zelf, vm)? { + while let PyIterReturn::Return(row) = Cursor::next(zelf, vm)? { list.push(row); if max_rows > 0 && list.len() as c_int >= max_rows { break;