From fb8758f36b61129d212b85778cbdb44502041629 Mon Sep 17 00:00:00 2001 From: Matthijs Berends <31037261+msberends@users.noreply.github.com> Date: Sun, 5 Apr 2026 17:26:21 +0200 Subject: [PATCH] fix: convert Python lists to R vectors in wrapper generator (#270) * fix: convert Python lists to R vectors in wrapper to prevent R list coercion errors Fixes #267. Python lists passed to R functions via rpy2 are received as R lists, not R character/numeric vectors. This causes is.mic(), is.sir(), is.disk() etc. to return length > 1 logicals, which break R's && operator. Added convert_to_r() helper that maps Python list/tuple to the appropriate typed R vector (StrVector, IntVector, FloatVector) based on element types. The r_to_python decorator now applies this to all args and kwargs before calling the R function. * docs: instruct Claude to install git and gh before computing version --- CLAUDE.md | 11 ++++++++- data-raw/_generate_python_wrapper.sh | 34 +++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4892f3c00..cce4f7a73 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -152,7 +152,16 @@ All PRs are **squash-merged**, so each PR lands as exactly **one commit** on the #### Computing the correct version number -Run the following from the repo root to determine the version string to use: +**First, ensure `git` and `gh` are installed** — both are required for the version computation and for pushing changes. Install them if missing before doing anything else: + +```bash +which git || apt-get install -y git +which gh || apt-get install -y gh +# Also ensure all tags are fetched so git describe works +git fetch --tags +``` + +Then run the following from the repo root to determine the version string to use: ```bash currenttag=$(git describe --tags --abbrev=0 | sed 's/v//') diff --git a/data-raw/_generate_python_wrapper.sh b/data-raw/_generate_python_wrapper.sh index a8b2ee01a..f56b139fd 100644 --- a/data-raw/_generate_python_wrapper.sh +++ b/data-raw/_generate_python_wrapper.sh @@ -141,6 +141,32 @@ import numpy as np # Import the AMR R package amr_r = importr('AMR') +def convert_to_r(value): + """Convert Python lists/tuples to typed R vectors. + + rpy2's default_converter passes Python lists to R as R lists, not as + character/numeric vectors. This causes element-wise type-check functions + such as is.mic(), is.sir(), and is.disk() to return a logical vector + rather than a single logical, breaking R's scalar && operator. + + This helper converts Python lists and tuples to the appropriate R vector + type based on the element types, so R always receives a proper vector.""" + if isinstance(value, (list, tuple)): + if len(value) == 0: + return StrVector([]) + # bool must be checked before int because bool is a subclass of int + if all(isinstance(v, bool) for v in value): + return robjects.vectors.BoolVector(value) + if all(isinstance(v, int) for v in value): + return IntVector(value) + if all(isinstance(v, float) for v in value): + return FloatVector(value) + if all(isinstance(v, str) for v in value): + return StrVector(value) + # Mixed types: coerce all to string + return StrVector([str(v) for v in value]) + return value + def convert_to_python(r_output): # Check if it's a StrVector (R character vector) if isinstance(r_output, StrVector): @@ -166,10 +192,13 @@ def convert_to_python(r_output): return r_output def r_to_python(r_func): - """Decorator that runs an rpy2 function under a localconverter - and then applies convert_to_python to its output.""" + """Decorator that converts Python list/tuple inputs to typed R vectors, + runs the rpy2 function under a localconverter, and converts the output + to a Python type.""" @functools.wraps(r_func) def wrapper(*args, **kwargs): + args = tuple(convert_to_r(a) for a in args) + kwargs = {k: convert_to_r(v) for k, v in kwargs.items()} with localconverter(default_converter + numpy2ri.converter + pandas2ri.converter): return convert_to_python(r_func(*args, **kwargs)) return wrapper @@ -312,4 +341,3 @@ cd ../PythonPackage/AMR pip3 install build python3 -m build # python3 setup.py sdist bdist_wheel -