diff --git a/NEWS.md b/NEWS.md index 98a35703..4931f0ec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,9 @@ - Updates for Positron to fix issues with `repl_python()` and Variables Pane (#1755). +- Fixed an issue where `[` received Python objects as slice arguments. + e.g., `x[start:end]` when `start` or `end` were Python objects (#1731). + - Reticulate-managed `uv` can now resolve system-installed Pythons, supporting platforms where pre-built binaries are unavailable, such as musl-based Alpine Linux (#1751, #1752). diff --git a/src/python.cpp b/src/python.cpp index cbdd0242..fccded54 100644 --- a/src/python.cpp +++ b/src/python.cpp @@ -4619,22 +4619,34 @@ PyObjectRef py_capsule(SEXP x) { PyObjectRef py_slice(SEXP start = R_NilValue, SEXP stop = R_NilValue, SEXP step = R_NilValue) { GILScope _gil; - PyObjectPtr start_, stop_, step_; + auto coerce_slice_arg = [](SEXP x) -> PyObject* { + if (x == R_NilValue) { + return NULL; + } + if (TYPEOF(x) == INTSXP || TYPEOF(x) == REALSXP) { + return PyLong_FromLong(Rf_asInteger(x)); + } + if (is_py_object(x)) { + PyObject* pyobj = PyObjectRef(x, false).get(); + Py_IncRef(pyobj); + return pyobj; + } + return r_to_py(x, false); + }; - if (start != R_NilValue) - start_.assign(PyLong_FromLong(Rf_asInteger(start))); - if (stop != R_NilValue) - stop_.assign(PyLong_FromLong(Rf_asInteger(stop))); - if (step != R_NilValue) - step_.assign(PyLong_FromLong(Rf_asInteger(step))); + PyObjectPtr start_(coerce_slice_arg(start)); + PyObjectPtr stop_(coerce_slice_arg(stop)); + PyObjectPtr step_(coerce_slice_arg(step)); - PyObject* out(PySlice_New(start_, stop_, step_)); + PyObject* out = PySlice_New(start_, stop_, step_); if (out == NULL) throw PythonException(py_fetch_error()); + return py_ref(out, false); } + //' @rdname iterate //' @export // [[Rcpp::export]] diff --git a/tests/testthat/test-python-base-r-generics.R b/tests/testthat/test-python-base-r-generics.R index 964fb890..c011d492 100644 --- a/tests/testthat/test-python-base-r-generics.R +++ b/tests/testthat/test-python-base-r-generics.R @@ -169,3 +169,24 @@ test_that("[ can infer slices, multiple args", { expect_identical(py_eval("x"), py_to_r(x)) }) + + +test_that("[ passes through python objects", { + + skip_if_no_numpy() + + np <- import("numpy", convert = FALSE) + + x <- np$arange(10L) + ir <- 3L + ip <- np$array(3L) + + expect_equal(py_to_r(x[ir]), 3L) + expect_equal(py_to_r(x[ip]), 3L) + + expect_equal(py_to_r(x[ir:NA]), array(3:9)) + expect_equal(py_to_r(x[ip:NA]), array(3:9)) + expect_equal(py_to_r(x[NA:ir]), array(0:2)) + expect_equal(py_to_r(x[NA:ip]), array(0:2)) + +})