From: Bastian Germann <bage@debian.org>
Date: Thu, 14 May 2026 17:44:06 +0200
Subject: [PATCH] Tolerate libR.so without R_nchar

CharSexp.nchar() now tolerates R builds where libR.so does not export R_nchar.
In sexp.py, the native symbol is cached with a safe lookup, and the tests fall
back to base R's nchar() when that symbol is missing. That keeps the fast
native path when available and fixes the failure you hit on systems with a
narrower exported C API.
---
--- a/rpy2-rinterface/src/rpy2/rinterface/tests/test_sexp.py
+++ b/rpy2-rinterface/src/rpy2/rinterface/tests/test_sexp.py
@@ -273,5 +273,12 @@ def test_charsxp_nchar():
     assert cs.nchar() == 0
 
 
+def test_charsxp_nchar_fallback(monkeypatch):
+    monkeypatch.setattr(rinterface.sexp.CharSexp, '_R_NCHAR', None)
+    v = rinterface.StrSexpVector(['abc', 'de', ''])
+    cs = v.get_charsxp(0)
+    assert cs.nchar() == 3
+
+
 def test_missingtype():
     assert not rinterface.MissingArg
--- a/rpy2-rinterface/src/rpy2/rinterface_lib/sexp.py
+++ b/rpy2-rinterface/src/rpy2/rinterface_lib/sexp.py
@@ -291,6 +291,7 @@ class CharSexp(Sexp):
     """R's internal (C API-level) scalar for strings."""
 
     _R_TYPE = openrlib.rlib.CHARSXP
+    _R_NCHAR = openrlib._get_symbol_or_fallback('R_nchar', None)
     _NCHAR_MSG = openrlib.ffi.new('char []', b'rpy2.rinterface.CharSexp.nchar')
 
     @property
@@ -304,14 +305,22 @@ class CharSexp(Sexp):
             openrlib.lock.release()
 
     def nchar(self, what: NCHAR_TYPE = NCHAR_TYPE.Bytes) -> int:
+        if self._R_NCHAR is None:
+            return int(
+                baseenv['nchar'](
+                    StrSexpVector([self]),
+                    type=what.name.lower(),
+                    allowNA=False,
+                    keepNA=False
+                )[0]
+            )
         try:
             openrlib.lock.acquire()
-            # TODO: nchar_type is not parsed properly by cffi ?
-            return openrlib.rlib.R_nchar(self.__sexp__._cdata,
-                                         what.value,
-                                         openrlib.rlib.FALSE,
-                                         openrlib.rlib.FALSE,
-                                         self._NCHAR_MSG)
+            return self._R_NCHAR(self.__sexp__._cdata,
+                                 what.value,
+                                 openrlib.rlib.FALSE,
+                                 openrlib.rlib.FALSE,
+                                 self._NCHAR_MSG)
         finally:
             openrlib.lock.release()
 
