def _evaluate_in_r(rargs: FFI.CData) -> FFI.CData: # An uncaught exception in the boby of this function would # result in a segfault. we wrap it in a try-except an report # exceptions as logs. rlib = openrlib.rlib try: rargs = rlib.CDR(rargs) cdata = rlib.CAR(rargs) if (_TYPEOF(cdata) != rlib.EXTPTRSXP): # TODO: also check tag # (rlib.R_ExternalPtrTag(sexp) == '.Python') logger.error('The fist item is not an R external pointer.') return rlib.R_NilValue handle = rlib.R_ExternalPtrAddr(cdata) func = ffi.from_handle(handle) pyargs = [] pykwargs = {} rargs = rlib.CDR(rargs) while rargs != rlib.R_NilValue: cdata = rlib.CAR(rargs) if rlib.Rf_isNull(rlib.TAG(rargs)): # Unnamed argument pyargs.append(conversion._cdata_to_rinterface(cdata)) else: # Named arguments rname = rlib.PRINTNAME(rlib.TAG(rargs)) name = conversion._cchar_to_str( rlib.R_CHAR(rname), conversion._R_ENC_PY[openrlib.rlib.Rf_getCharCE(rname)]) pykwargs[name] = conversion._cdata_to_rinterface(cdata) rargs = rlib.CDR(rargs) res = func(*pyargs, **pykwargs) # The object is whatever the "rternalized" function `func` # is returning and we need to cast that result into a SEXP # that R's C API can handle. At the same time we need to ensure # that the R is: # - protected from garbage collection long enough to let the R # code that called the rternalized function complete. # - eventually its memory is freed to prevent a leak. # To that end, we create a SEXP object to be returned that is # not managed by rpy2, leaving the object's lifespan under R's # sole control. if (hasattr(res, '_sexpobject') and isinstance(res._sexpobject, SexpCapsule)): return res._sexpobject._cdata else: return conversion._python_to_cdata(res) except Exception as e: logger.error('%s: %s' % (type(e), e)) return rlib.R_NilValue
def __getitem__(self, i: Union[int, slice]) -> Sexp: cdata = self.__sexp__._cdata rlib = openrlib.rlib if isinstance(i, int): # R-exts says that it is converted to a VECSXP when subsetted. i_c = _rinterface._python_index_to_c(cdata, i) item_cdata = rlib.Rf_nthcdr(cdata, i_c) with memorymanagement.rmemory() as rmemory: res_cdata = rmemory.protect( rlib.Rf_allocVector(RTYPES.VECSXP, 1)) rlib.SET_VECTOR_ELT( res_cdata, 0, rlib.CAR( item_cdata )) res_name = rmemory.protect( rlib.Rf_allocVector(RTYPES.STRSXP, 1)) item_cdata_name = rlib.PRINTNAME(rlib.TAG(item_cdata)) if _rinterface._TYPEOF(item_cdata_name) != rlib.NILSXP: rlib.SET_STRING_ELT( res_name, 0, item_cdata_name) rlib.Rf_namesgets(res_cdata, res_name) res = conversion._cdata_to_rinterface(res_cdata) elif isinstance(i, slice): iter_indices = range(*i.indices(len(self))) n = len(iter_indices) with memorymanagement.rmemory() as rmemory: res_cdata = rmemory.protect( rlib.Rf_allocVector( self._R_TYPE, n) ) iter_res_cdata = res_cdata prev_i = 0 lst_cdata = self.__sexp__._cdata for i in iter_indices: if i >= len(self): raise IndexError('index out of range') lst_cdata = rlib.Rf_nthcdr(lst_cdata, i - prev_i) prev_i = i rlib.SETCAR(iter_res_cdata, rlib.CAR(lst_cdata)) rlib.SET_TAG(iter_res_cdata, rlib.TAG(lst_cdata)) iter_res_cdata = rlib.CDR(iter_res_cdata) res = conversion._cdata_to_rinterface(res_cdata) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) return res
def rclass_get(scaps: _rinterface.CapsuleBase) -> StrSexpVector: rlib = openrlib.rlib with memorymanagement.rmemory() as rmemory: classes = rmemory.protect( rlib.Rf_getAttrib(scaps._cdata, rlib.R_ClassSymbol)) if rlib.Rf_length(classes) == 0: dim = rmemory.protect( rlib.Rf_getAttrib(scaps._cdata, rlib.R_DimSymbol)) ndim = rlib.Rf_length(dim) if ndim > 0: if ndim == 2: classname = 'matrix' else: classname = 'array' else: typeof = RTYPES(scaps.typeof) classname = _DEFAULT_RCLASS_NAMES.get( typeof, str(typeof)) classes = StrSexpVector.from_iterable( [classname]) else: classes = conversion._cdata_to_rinterface(classes) return classes
def rclass_get(scaps: _rinterface.CapsuleBase) -> StrSexpVector: """ Get the R class name. If no specific attribute "class" is defined from the objects, this will perform the equivalent of R_data_class() (src/main/attrib.c in the R source code). """ rlib = openrlib.rlib with memorymanagement.rmemory() as rmemory: classes = rmemory.protect( rlib.Rf_getAttrib(scaps._cdata, rlib.R_ClassSymbol)) if rlib.Rf_length(classes) == 0: classname: typing.Tuple[str, ...] dim = rmemory.protect( rlib.Rf_getAttrib(scaps._cdata, rlib.R_DimSymbol)) ndim = rlib.Rf_length(dim) if ndim > 0: if ndim == 2: if int(RVersion()['major']) >= 4: classname = ('matrix', 'array') else: classname = ('matrix', ) else: classname = ('array', ) else: typeof = RTYPES(scaps.typeof) if typeof in (RTYPES.CLOSXP, RTYPES.SPECIALSXP, RTYPES.BUILTINSXP): classname = ('function', ) elif typeof == RTYPES.REALSXP: classname = ('numeric', ) elif typeof == RTYPES.SYMSXP: classname = ('name', ) elif typeof == RTYPES.LANGSXP: symb = rlib.CAR(scaps._cdata) if openrlib.rlib.Rf_isSymbol(symb): symb_rstr = openrlib.rlib.PRINTNAME(symb) symb_str = conversion._cchar_to_str( openrlib.rlib.R_CHAR(symb_rstr), conversion._R_ENC_PY[openrlib.rlib .Rf_getCharCE(symb_rstr)] ) if symb_str in ('if', 'while', 'for', '=', '<-', '(', '{'): classname = (symb_str, ) else: classname = ('call', ) else: classname = ('call', ) else: classname = (_TYPE2STR.get(typeof, str(typeof)), ) classes = StrSexpVector.from_iterable(classname) else: classes = conversion._cdata_to_rinterface(classes) return classes
def __getitem__( self, i: typing.Union[int, slice]) -> typing.Union[Sexp, VT, typing.Any]: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) res = conversion._cdata_to_rinterface( self._R_VECTOR_ELT(cdata, i_c)) elif isinstance(i, slice): res = self.from_iterable([ self._R_VECTOR_ELT( cdata, i_c, ) for i_c in range(*i.indices(len(self))) ], cast_value=lambda x: x) else: raise TypeError('Indices must be integers or slices, not %s' % type(i)) return res