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
示例#2
0
 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
示例#3
0
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
示例#4
0
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
示例#5
0
 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