1import atexit 2import contextlib 3import os 4import math 5import platform 6import signal 7import textwrap 8import threading 9import typing 10import warnings 11from typing import Union 12from rpy2.rinterface_lib import openrlib 13import rpy2.rinterface_lib._rinterface_capi as _rinterface 14import rpy2.rinterface_lib.embedded as embedded 15import rpy2.rinterface_lib.conversion as conversion 16from rpy2.rinterface_lib.conversion import _cdata_res_to_rinterface 17import rpy2.rinterface_lib.memorymanagement as memorymanagement 18from rpy2.rinterface_lib import na_values 19from rpy2.rinterface_lib.sexp import NULLType 20import rpy2.rinterface_lib.bufferprotocol as bufferprotocol 21from rpy2.rinterface_lib import sexp 22from rpy2.rinterface_lib.sexp import CharSexp # noqa: F401 23from rpy2.rinterface_lib.sexp import RTYPES 24from rpy2.rinterface_lib.sexp import SexpVector 25from rpy2.rinterface_lib.sexp import StrSexpVector 26from rpy2.rinterface_lib.sexp import Sexp 27from rpy2.rinterface_lib.sexp import SexpEnvironment 28from rpy2.rinterface_lib.sexp import unserialize # noqa: F401 29from rpy2.rinterface_lib.sexp import emptyenv 30from rpy2.rinterface_lib.sexp import baseenv 31from rpy2.rinterface_lib.sexp import globalenv 32 33if os.name == 'nt': 34 import rpy2.rinterface_lib.embedded_mswin as embedded_mswin 35 embedded._initr = embedded_mswin._initr_win32 36 37R_NilValue = openrlib.rlib.R_NilValue 38 39endr = embedded.endr 40 41_evaluation_context = globalenv 42 43 44def get_evaluation_context() -> SexpEnvironment: 45 """Get the frame (environment) in which R code is currently evaluated.""" 46 return _evaluation_context 47 48 49@contextlib.contextmanager 50def local_context( 51 env: typing.Optional[SexpEnvironment] = None, 52 use_rlock: bool = True 53) -> typing.Iterator[SexpEnvironment]: 54 """Local context for the evaluation of R code. 55 56 Args: 57 - env: an environment to use as a context. If not specified (None, the 58 default), a child environment to the current context is created. 59 - use_rlock: whether to use a threading lock (see the documentation about 60 "rlock". The default is True. 61 62 Returns: 63 Yield the environment (passed to env, or created). 64 """ 65 66 global _evaluation_context 67 68 parent_frame = _evaluation_context 69 if env is None: 70 env = baseenv['new.env']( 71 baseenv['parent.frame']() 72 if parent_frame is None 73 else parent_frame) 74 try: 75 if use_rlock: 76 with openrlib.rlock: 77 _evaluation_context = env 78 yield env 79 else: 80 _evaluation_context = env 81 yield env 82 finally: 83 _evaluation_context = parent_frame 84 85 86def _sigint_handler(sig, frame): 87 raise KeyboardInterrupt() 88 89 90@_cdata_res_to_rinterface 91def parse(text: str, num: int = -1): 92 """Parse a string as R code. 93 94 :param:`text` A string with R code to parse. 95 :param:`num` The maximum number of lines to parse. If -1, no 96 limit is applied. 97 """ 98 99 if not isinstance(text, str): 100 raise TypeError('text must be a string.') 101 robj = StrSexpVector([text]) 102 with memorymanagement.rmemory() as rmemory: 103 res = _rinterface._parse(robj.__sexp__._cdata, num, rmemory) 104 return res 105 106 107def evalr(source: str, maxlines: int = -1) -> sexp.Sexp: 108 """Evaluate a string as R code. 109 110 Evaluate a string as R just as it would happen when writing 111 code in an R terminal. 112 113 :param:`text` A string to be evaluated as R code. 114 :param:`maxlines` The maximum number of lines to parse. If -1, no 115 limit is applied.""" 116 117 res = parse(source, num=maxlines) 118 res = baseenv['eval'](res) 119 return res 120 121 122def vector_memoryview(obj: sexp.SexpVector, 123 sizeof_str: str, cast_str: str) -> memoryview: 124 """ 125 :param:`obj` R vector 126 :param:`sizeof_str` Type in a string to use with ffi.sizeof() 127 (for example "int") 128 :param:`cast_str` Type in a string to use with memoryview.cast() 129 (for example "i") 130 """ 131 b = openrlib.ffi.buffer( 132 obj._R_GET_PTR(obj.__sexp__._cdata), 133 openrlib.ffi.sizeof(sizeof_str) * len(obj)) 134 shape = bufferprotocol.getshape(obj.__sexp__._cdata) 135 # One could have expected to only need builtin Python 136 # and do something like 137 # ``` 138 # mv = memoryview(b).cast(cast_str, shape, order='F') 139 # ``` 140 # but Python does not handle FORTRAN-ordered arrays without having 141 # to write C extensions. We have to use numpy. 142 # TODO: Having numpy a requirement just for this is a problem. 143 # TODO: numpy needed for memoryview 144 # (as long as https://bugs.python.org/issue34778 not resolved) 145 import numpy 146 a = numpy.frombuffer(b, dtype=cast_str).reshape(shape, order='F') 147 mv = memoryview(a) 148 return mv 149 150 151class SexpSymbol(sexp.Sexp): 152 """An unevaluated R symbol.""" 153 154 def __init__( 155 self, 156 obj: Union[Sexp, 157 _rinterface.SexpCapsule, 158 _rinterface.UninitializedRCapsule, 159 str] 160 ): 161 if isinstance(obj, Sexp) or isinstance(obj, _rinterface.CapsuleBase): 162 super().__init__(obj) 163 elif isinstance(obj, str): 164 name_cdata = _rinterface.ffi.new('char []', obj.encode('utf-8')) 165 sexp = _rinterface.SexpCapsule( 166 openrlib.rlib.Rf_install(name_cdata)) 167 super().__init__(sexp) 168 else: 169 raise TypeError( 170 'The constructor must be called ' 171 'with that is an instance of rpy2.rinterface.sexp.Sexp ' 172 'or an instance of rpy2.rinterface._rinterface.SexpCapsule') 173 174 def __str__(self) -> str: 175 return conversion._cchar_to_str( 176 openrlib._STRING_VALUE( 177 self._sexpobject._cdata 178 ), 'utf-8' 179 ) 180 181 182class _MissingArgType(SexpSymbol, metaclass=sexp.SingletonABC): 183 184 def __init__(self): 185 if embedded.isready(): 186 tmp = sexp.Sexp( 187 _rinterface.UnmanagedSexpCapsule( 188 openrlib.rlib.R_MissingArg 189 ) 190 ) 191 else: 192 tmp = sexp.Sexp( 193 _rinterface.UninitializedRCapsule(RTYPES.SYMSXP.value) 194 ) 195 super().__init__(tmp) 196 197 def __bool__(self) -> bool: 198 """This is always False.""" 199 return False 200 201 @property 202 def __sexp__(self) -> _rinterface.SexpCapsule: 203 return self._sexpobject 204 205 206MissingArg = _MissingArgType() 207 208 209class SexpPromise(Sexp): 210 211 @_cdata_res_to_rinterface 212 def eval(self, env: typing.Optional[SexpEnvironment] = None) -> sexp.Sexp: 213 """"Evalute the R "promise". 214 215 :param:`env` The environment in which to evaluate the 216 promise. 217 """ 218 if env is None: 219 env = globalenv 220 return openrlib.rlib.Rf_eval(self.__sexp__._cdata, env) 221 222 223NPCOMPAT_TYPE = typing.TypeVar('NPCOMPAT_TYPE', 224 'ByteSexpVector', 225 'BoolSexpVector', 226 'IntSexpVector', 227 'FloatSexpVector') 228 229 230class NumpyArrayMixin: 231 """Numpy-specific API for accessing the content of a numpy array. 232 233 This interface implements version 3 of Numpy's `__array_interface__` 234 and is only available / possible for some of the R vectors.""" 235 236 @property 237 def __array_interface__( 238 self: NPCOMPAT_TYPE 239 ) -> dict: 240 """Return an `__array_interface__` version 3. 241 242 Note that the pointer returned in the items 'data' corresponds to 243 a memory area under R's memory management and that it will become 244 invalid once the area once R frees the object. It is safer to keep 245 the rpy2 object proxying the R object alive for the duration the 246 pointer is used in Python / numpy.""" 247 shape = bufferprotocol.getshape(self.__sexp__._cdata) 248 data = openrlib.ffi.buffer(self._R_GET_PTR(self.__sexp__._cdata)) 249 strides = bufferprotocol.getstrides(self.__sexp__._cdata, 250 shape, 251 self._R_SIZEOF_ELT) 252 return {'shape': shape, 253 'typestr': self._NP_TYPESTR, 254 'strides': strides, 255 'data': data, 256 'version': 3} 257 258 259class ByteSexpVector(NumpyArrayMixin, SexpVector): 260 """Array of bytes. 261 262 This is the R equivalent to a Python :class:`bytesarray`. 263 """ 264 265 _R_TYPE = openrlib.rlib.RAWSXP 266 _R_SIZEOF_ELT = _rinterface.ffi.sizeof('char') 267 _NP_TYPESTR = '|u1' 268 269 _R_GET_PTR = staticmethod(openrlib.RAW) 270 271 @staticmethod 272 def _CAST_IN(x: typing.Any) -> int: 273 if isinstance(x, int): 274 if x > 255: 275 raise ValueError('byte must be in range(0, 256)') 276 res = x 277 elif isinstance(x, (bytes, bytearray)): 278 if len(x) != 1: 279 raise ValueError('byte must be a single character') 280 res = ord(x) 281 else: 282 raise ValueError('byte must be an integer [0, 255] or a ' 283 'single byte character') 284 return res 285 286 @staticmethod 287 def _R_VECTOR_ELT(x, i: int) -> None: 288 return openrlib.RAW(x)[i] 289 290 @staticmethod 291 def _R_SET_VECTOR_ELT(x, i: int, val) -> None: 292 openrlib.RAW(x)[i] = val 293 294 def __getitem__(self, 295 i: Union[int, slice]) -> Union[int, 'ByteSexpVector']: 296 297 cdata = self.__sexp__._cdata 298 if isinstance(i, int): 299 i_c = _rinterface._python_index_to_c(cdata, i) 300 res = openrlib.RAW_ELT(cdata, i_c) 301 elif isinstance(i, slice): 302 res = type(self).from_iterable( 303 [openrlib.RAW_ELT( 304 cdata, i_c 305 ) for i_c in range(*i.indices(len(self))) 306 ] 307 ) 308 else: 309 raise TypeError( 310 'Indices must be integers or slices, not %s' % type(i)) 311 return res 312 313 def __setitem__(self, i: Union[int, slice], value) -> None: 314 cdata = self.__sexp__._cdata 315 if isinstance(i, int): 316 i_c = _rinterface._python_index_to_c(cdata, i) 317 openrlib.RAW(cdata)[i_c] = self._CAST_IN(value) 318 elif isinstance(i, slice): 319 for i_c, v in zip(range(*i.indices(len(self))), value): 320 if v > 255: 321 raise ValueError('byte must be in range(0, 256)') 322 openrlib.RAW(cdata)[i_c] = self._CAST_IN(v) 323 else: 324 raise TypeError( 325 'Indices must be integers or slices, not %s' % type(i)) 326 327 328class BoolSexpVector(NumpyArrayMixin, SexpVector): 329 """Array of booleans. 330 331 Note that R is internally storing booleans as integers to 332 allow an additional "NA" value to represent missingness.""" 333 334 _R_TYPE = openrlib.rlib.LGLSXP 335 _R_SIZEOF_ELT = _rinterface.ffi.sizeof('Rboolean') 336 _NP_TYPESTR = '|i' 337 _R_VECTOR_ELT = openrlib.LOGICAL_ELT 338 _R_SET_VECTOR_ELT = openrlib.SET_LOGICAL_ELT 339 _R_GET_PTR = staticmethod(openrlib.LOGICAL) 340 341 @staticmethod 342 def _CAST_IN(x): 343 if x is None or x == openrlib.rlib.R_NaInt: 344 return NA_Logical 345 else: 346 return bool(x) 347 348 def __getitem__(self, i: Union[int, slice]) -> Union[typing.Optional[bool], 349 'BoolSexpVector']: 350 cdata = self.__sexp__._cdata 351 if isinstance(i, int): 352 i_c = _rinterface._python_index_to_c(cdata, i) 353 elt = openrlib.LOGICAL_ELT(cdata, i_c) 354 res = na_values.NA_Logical if elt == NA_Logical else bool(elt) 355 elif isinstance(i, slice): 356 res = type(self).from_iterable( 357 [openrlib.LOGICAL_ELT(cdata, i_c) 358 for i_c in range(*i.indices(len(self)))] 359 ) 360 else: 361 raise TypeError( 362 'Indices must be integers or slices, not %s' % type(i)) 363 return res 364 365 def __setitem__(self, i: Union[int, slice], value) -> None: 366 cdata = self.__sexp__._cdata 367 if isinstance(i, int): 368 i_c = _rinterface._python_index_to_c(cdata, i) 369 openrlib.SET_LOGICAL_ELT(cdata, i_c, 370 int(value)) 371 elif isinstance(i, slice): 372 for i_c, v in zip(range(*i.indices(len(self))), value): 373 openrlib.SET_LOGICAL_ELT(cdata, i_c, 374 int(v)) 375 else: 376 raise TypeError( 377 'Indices must be integers or slices, not %s' % type(i)) 378 379 def memoryview(self) -> memoryview: 380 return vector_memoryview(self, 'int', 'i') 381 382 383def nullable_int(v): 384 if type(v) is float and math.isnan(v): 385 return openrlib.rlib.R_NaInt 386 else: 387 return int(v) 388 389 390class IntSexpVector(NumpyArrayMixin, SexpVector): 391 392 _R_TYPE = openrlib.rlib.INTSXP 393 _R_SET_VECTOR_ELT = openrlib.SET_INTEGER_ELT 394 _R_VECTOR_ELT = openrlib.INTEGER_ELT 395 _R_SIZEOF_ELT = _rinterface.ffi.sizeof('int') 396 _NP_TYPESTR = '|i' 397 398 _R_GET_PTR = staticmethod(openrlib.INTEGER) 399 _CAST_IN = staticmethod(nullable_int) 400 401 def __getitem__(self, i: Union[int, slice]) -> Union[int, 'IntSexpVector']: 402 cdata = self.__sexp__._cdata 403 if isinstance(i, int): 404 i_c = _rinterface._python_index_to_c(cdata, i) 405 res = openrlib.INTEGER_ELT(cdata, i_c) 406 if res == NA_Integer: 407 res = NA_Integer 408 elif isinstance(i, slice): 409 res = type(self).from_iterable( 410 [openrlib.INTEGER_ELT( 411 cdata, i_c 412 ) for i_c in range(*i.indices(len(self)))] 413 ) 414 else: 415 raise TypeError( 416 'Indices must be integers or slices, not %s' % type(i)) 417 return res 418 419 def __setitem__(self, i: Union[int, slice], value) -> None: 420 cdata = self.__sexp__._cdata 421 if isinstance(i, int): 422 i_c = _rinterface._python_index_to_c(cdata, i) 423 openrlib.SET_INTEGER_ELT(cdata, i_c, 424 int(value)) 425 elif isinstance(i, slice): 426 for i_c, v in zip(range(*i.indices(len(self))), value): 427 openrlib.SET_INTEGER_ELT(cdata, i_c, 428 int(v)) 429 else: 430 raise TypeError( 431 'Indices must be integers or slices, not %s' % type(i)) 432 433 def memoryview(self) -> memoryview: 434 return vector_memoryview(self, 'int', 'i') 435 436 437class FloatSexpVector(NumpyArrayMixin, SexpVector): 438 439 _R_TYPE = openrlib.rlib.REALSXP 440 _R_VECTOR_ELT = openrlib.REAL_ELT 441 _R_SET_VECTOR_ELT = openrlib.SET_REAL_ELT 442 _R_SIZEOF_ELT = _rinterface.ffi.sizeof('double') 443 _NP_TYPESTR = '|d' 444 445 _CAST_IN = staticmethod(float) 446 _R_GET_PTR = staticmethod(openrlib.REAL) 447 448 def __getitem__( 449 self, i: Union[int, slice] 450 ) -> Union[float, 'FloatSexpVector']: 451 cdata = self.__sexp__._cdata 452 if isinstance(i, int): 453 i_c = _rinterface._python_index_to_c(cdata, i) 454 res = openrlib.REAL_ELT(cdata, i_c) 455 elif isinstance(i, slice): 456 res = type(self).from_iterable( 457 [openrlib.REAL_ELT( 458 cdata, i_c) for i_c in range(*i.indices(len(self)))] 459 ) 460 else: 461 raise TypeError('Indices must be integers or slices, not %s' % 462 type(i)) 463 return res 464 465 def __setitem__(self, i: Union[int, slice], value) -> None: 466 cdata = self.__sexp__._cdata 467 if isinstance(i, int): 468 i_c = _rinterface._python_index_to_c(cdata, i) 469 openrlib.SET_REAL_ELT(cdata, i_c, 470 float(value)) 471 elif isinstance(i, slice): 472 for i_c, v in zip(range(*i.indices(len(self))), value): 473 openrlib.SET_REAL_ELT(cdata, i_c, 474 float(v)) 475 else: 476 raise TypeError( 477 'Indices must be integers or slices, not %s' % type(i)) 478 479 def memoryview(self) -> memoryview: 480 return vector_memoryview(self, 'double', 'd') 481 482 483class ComplexSexpVector(SexpVector): 484 485 _R_TYPE = openrlib.rlib.CPLXSXP 486 _R_GET_PTR = staticmethod(openrlib.COMPLEX) 487 _R_SIZEOF_ELT = _rinterface.ffi.sizeof('Rcomplex') 488 489 @staticmethod 490 def _R_VECTOR_ELT(x, i): 491 return openrlib.COMPLEX(x)[i] 492 493 @staticmethod 494 def _R_SET_VECTOR_ELT(x, i, v): 495 openrlib.COMPLEX(x).__setitem__(i, v) 496 497 @staticmethod 498 def _CAST_IN(x): 499 if isinstance(x, complex): 500 res = (x.real, x.imag) 501 else: 502 try: 503 res = (x.r, x.i) 504 except AttributeError: 505 raise TypeError( 506 'Unable to turn value into an R complex number.' 507 ) 508 return res 509 510 def __getitem__( 511 self, i: Union[int, slice] 512 ) -> Union[complex, 'ComplexSexpVector']: 513 cdata = self.__sexp__._cdata 514 if isinstance(i, int): 515 i_c = _rinterface._python_index_to_c(cdata, i) 516 _ = openrlib.COMPLEX_ELT(cdata, i_c) 517 res = complex(_.r, _.i) 518 elif isinstance(i, slice): 519 res = type(self).from_iterable( 520 [openrlib.COMPLEX_ELT( 521 cdata, i_c) for i_c in range(*i.indices(len(self)))] 522 ) 523 else: 524 raise TypeError( 525 'Indices must be integers or slices, not %s' % type(i)) 526 return res 527 528 def __setitem__(self, i: Union[int, slice], value) -> None: 529 cdata = self.__sexp__._cdata 530 if isinstance(i, int): 531 i_c = _rinterface._python_index_to_c(cdata, i) 532 openrlib.COMPLEX(cdata)[i_c] = self._CAST_IN(value) 533 elif isinstance(i, slice): 534 for i_c, v in zip(range(*i.indices(len(self))), value): 535 openrlib.COMPLEX(cdata)[i_c] = self._CAST_IN(v) 536 else: 537 raise TypeError( 538 'Indices must be integers or slices, not %s' % type(i)) 539 540 541class ListSexpVector(SexpVector): 542 """R list. 543 544 An R list an R vector (array) that is similar to a Python list in 545 the sense that items in the list can be of any type, whereas most 546 other R vectors are homogeneous (all items are of the same type). 547 """ 548 _R_TYPE = openrlib.rlib.VECSXP 549 _R_GET_PTR = staticmethod(openrlib._VECTOR_PTR) 550 _R_SIZEOF_ELT = None 551 _R_VECTOR_ELT = openrlib.rlib.VECTOR_ELT 552 _R_SET_VECTOR_ELT = openrlib.rlib.SET_VECTOR_ELT 553 _CAST_IN = staticmethod(conversion._get_cdata) 554 555 556class PairlistSexpVector(SexpVector): 557 """R pairlist. 558 559 A R pairlist is rarely used outside of R's internal libraries and 560 a relatively small number of use cases. It is essentially a LISP-like 561 list of (name, value) pairs. 562 """ 563 _R_TYPE = openrlib.rlib.LISTSXP 564 _R_GET_PTR = None 565 _R_SIZEOF_ELT = None 566 _R_VECTOR_ELT = None 567 _R_SET_VECTOR_ELT = None 568 _CAST_IN = staticmethod(conversion._get_cdata) 569 570 def __getitem__(self, i: Union[int, slice]) -> Sexp: 571 cdata = self.__sexp__._cdata 572 rlib = openrlib.rlib 573 if isinstance(i, int): 574 # R-exts says that it is converted to a VECSXP when subsetted. 575 i_c = _rinterface._python_index_to_c(cdata, i) 576 item_cdata = rlib.Rf_nthcdr(cdata, i_c) 577 with memorymanagement.rmemory() as rmemory: 578 res_cdata = rmemory.protect( 579 rlib.Rf_allocVector(RTYPES.VECSXP, 1)) 580 rlib.SET_VECTOR_ELT( 581 res_cdata, 582 0, 583 rlib.CAR( 584 item_cdata 585 )) 586 res_name = rmemory.protect( 587 rlib.Rf_allocVector(RTYPES.STRSXP, 1)) 588 item_cdata_name = rlib.PRINTNAME(rlib.TAG(item_cdata)) 589 if _rinterface._TYPEOF(item_cdata_name) != rlib.NILSXP: 590 rlib.SET_STRING_ELT( 591 res_name, 592 0, 593 item_cdata_name) 594 rlib.Rf_namesgets(res_cdata, res_name) 595 res = conversion._cdata_to_rinterface(res_cdata) 596 elif isinstance(i, slice): 597 iter_indices = range(*i.indices(len(self))) 598 n = len(iter_indices) 599 with memorymanagement.rmemory() as rmemory: 600 res_cdata = rmemory.protect( 601 rlib.Rf_allocVector( 602 self._R_TYPE, n) 603 ) 604 iter_res_cdata = res_cdata 605 prev_i = 0 606 lst_cdata = self.__sexp__._cdata 607 for i in iter_indices: 608 if i >= len(self): 609 raise IndexError('index out of range') 610 lst_cdata = rlib.Rf_nthcdr(lst_cdata, i - prev_i) 611 prev_i = i 612 rlib.SETCAR(iter_res_cdata, 613 rlib.CAR(lst_cdata)) 614 rlib.SET_TAG(iter_res_cdata, 615 rlib.TAG(lst_cdata)) 616 iter_res_cdata = rlib.CDR(iter_res_cdata) 617 res = conversion._cdata_to_rinterface(res_cdata) 618 else: 619 raise TypeError( 620 'Indices must be integers or slices, not %s' % type(i)) 621 return res 622 623 @classmethod 624 @_cdata_res_to_rinterface 625 def from_iterable(cls, iterable, cast_in=None): 626 raise NotImplementedError() 627 628 629class ExprSexpVector(SexpVector): 630 _R_TYPE = openrlib.rlib.EXPRSXP 631 _R_GET_PTR = None 632 _CAST_IN = None 633 _R_SIZEOF_ELT = None 634 _R_VECTOR_ELT = openrlib.rlib.VECTOR_ELT 635 _R_SET_VECTOR_ELT = None 636 637 638class LangSexpVector(SexpVector): 639 _R_TYPE = openrlib.rlib.LANGSXP 640 _R_GET_PTR = None 641 _CAST_IN = None 642 _R_SIZEOF_ELT = None 643 _R_VECTOR_ELT = None 644 _R_SET_VECTOR_ELT = None 645 646 @_cdata_res_to_rinterface 647 def __getitem__(self, i: int): 648 cdata = self.__sexp__._cdata 649 i_c = _rinterface._python_index_to_c(cdata, i) 650 return openrlib.rlib.CAR( 651 openrlib.rlib.Rf_nthcdr(cdata, i_c) 652 ) 653 654 def __setitem__(self, i: int, value: sexp.SupportsSEXP) -> None: 655 cdata = self.__sexp__._cdata 656 i_c = _rinterface._python_index_to_c(cdata, i) 657 openrlib.rlib.SETCAR( 658 openrlib.rlib.Rf_nthcdr(cdata, i_c), 659 value.__sexp__._cdata 660 ) 661 662 663class SexpClosure(Sexp): 664 665 @_cdata_res_to_rinterface 666 def __call__(self, *args, **kwargs) -> Sexp: 667 error_occured = _rinterface.ffi.new('int *', 0) 668 with memorymanagement.rmemory() as rmemory: 669 call_r = rmemory.protect( 670 _rinterface.build_rcall(self.__sexp__._cdata, args, 671 kwargs.items())) 672 call_context = _evaluation_context 673 res = rmemory.protect( 674 openrlib.rlib.R_tryEval( 675 call_r, 676 call_context.__sexp__._cdata, 677 error_occured) 678 ) 679 if error_occured[0]: 680 raise embedded.RRuntimeError(_rinterface._geterrmessage()) 681 return res 682 683 @_cdata_res_to_rinterface 684 def rcall(self, keyvals, 685 environment: typing.Optional[SexpEnvironment] = None): 686 """Call/evaluate an R function. 687 688 Args: 689 - keyvals: a sequence of key/value (name/parameter) pairs. A 690 name/parameter that is None will indicated an unnamed parameter. 691 Like in R, keys/names do not have to be unique, partial matching 692 can be used, and named/unnamed parameters can occur at any position 693 in the sequence. 694 - environment: an optional R environment to evaluate the function. 695 """ 696 # TODO: check keyvals are pairs ? 697 if environment is None: 698 environment = _evaluation_context 699 assert isinstance(environment, SexpEnvironment) 700 error_occured = _rinterface.ffi.new('int *', 0) 701 702 with memorymanagement.rmemory() as rmemory: 703 call_r = rmemory.protect( 704 _rinterface.build_rcall(self.__sexp__._cdata, [], 705 keyvals)) 706 res = rmemory.protect( 707 openrlib.rlib.R_tryEval(call_r, 708 environment.__sexp__._cdata, 709 error_occured)) 710 if error_occured[0]: 711 raise embedded.RRuntimeError(_rinterface._geterrmessage()) 712 return res 713 714 @property 715 @_cdata_res_to_rinterface 716 def closureenv(self) -> SexpEnvironment: 717 """Closure of the R function.""" 718 return openrlib.rlib.CLOENV(self.__sexp__._cdata) 719 720 721class SexpS4(Sexp): 722 """R "S4" object.""" 723 pass 724 725 726# TODO: clean up 727def make_extptr(obj, tag, protected): 728 if protected is None: 729 cdata_protected = openrlib.rlib.R_NilValue 730 else: 731 try: 732 cdata_protected = protected.__sexp__._cdata 733 except AttributeError: 734 raise TypeError('Argument protected must inherit from %s' % 735 type(Sexp)) 736 737 ptr = _rinterface.ffi.new_handle(obj) 738 with memorymanagement.rmemory() as rmemory: 739 cdata = rmemory.protect( 740 openrlib.rlib.R_MakeExternalPtr( 741 ptr, 742 tag, 743 cdata_protected)) 744 openrlib.rlib.R_RegisterCFinalizer( 745 cdata, 746 (_rinterface._capsule_finalizer_c 747 if _rinterface._capsule_finalizer_c 748 else _rinterface._capsule_finalizer)) 749 res = _rinterface.SexpCapsuleWithPassenger(cdata, obj, ptr) 750 return res 751 752 753class SexpExtPtr(Sexp): 754 755 TYPE_TAG = 'Python' 756 757 @classmethod 758 def from_pyobject(cls, func, tag: str = TYPE_TAG, 759 protected=None): 760 if not isinstance(tag, str): 761 raise TypeError('The tag must be a string.') 762 scaps = make_extptr(func, 763 conversion._str_to_charsxp(cls.TYPE_TAG), 764 protected) 765 res = cls(scaps) 766 if tag != cls.TYPE_TAG: 767 res.TYPE_TAG = tag 768 return res 769 770 771# TODO: Only use rinterface-level ? 772conversion._R_RPY2_MAP.update({ 773 openrlib.rlib.NILSXP: NULLType, 774 openrlib.rlib.EXPRSXP: ExprSexpVector, 775 openrlib.rlib.LANGSXP: LangSexpVector, 776 openrlib.rlib.ENVSXP: SexpEnvironment, 777 openrlib.rlib.RAWSXP: ByteSexpVector, 778 openrlib.rlib.LGLSXP: BoolSexpVector, 779 openrlib.rlib.INTSXP: IntSexpVector, 780 openrlib.rlib.REALSXP: FloatSexpVector, 781 openrlib.rlib.CPLXSXP: ComplexSexpVector, 782 openrlib.rlib.STRSXP: StrSexpVector, 783 openrlib.rlib.VECSXP: ListSexpVector, 784 openrlib.rlib.LISTSXP: PairlistSexpVector, 785 openrlib.rlib.CLOSXP: SexpClosure, 786 openrlib.rlib.BUILTINSXP: SexpClosure, 787 openrlib.rlib.SPECIALSXP: SexpClosure, 788 openrlib.rlib.EXTPTRSXP: SexpExtPtr, 789 openrlib.rlib.SYMSXP: SexpSymbol, 790 openrlib.rlib.S4SXP: SexpS4 791 }) 792conversion._R_RPY2_DEFAULT_MAP = Sexp 793 794conversion._PY_RPY2_MAP.update({ 795 int: conversion._int_to_sexp, 796 float: conversion._float_to_sexp, 797 complex: conversion._complex_to_sexp 798 }) 799 800conversion._PY_R_MAP.update({ 801 _rinterface.ffi.CData: False, 802 # integer 803 int: conversion._int_to_sexp, 804 sexp.NAIntegerType: conversion._int_to_sexp, 805 # float 806 float: conversion._float_to_sexp, 807 sexp.NARealType: conversion._float_to_sexp, 808 # boolean 809 bool: conversion._bool_to_sexp, 810 sexp.NALogicalType: conversion._bool_to_sexp, 811 # string 812 str: conversion._str_to_sexp, 813 sexp.CharSexp: None, 814 sexp.NACharacterType: None, 815 # complex 816 complex: conversion._complex_to_sexp, 817 sexp.NAComplexType: conversion._complex_to_sexp, 818 # None 819 type(None): lambda x: openrlib.rlib.R_NilValue}) 820 821 822def vector(iterable, rtype: RTYPES) -> SexpVector: 823 """Create an R vector. 824 825 While the different types of R vectors all have their own class, 826 the creation of array in Python is often available through factory 827 function that accept the type of the array as a parameters. This 828 function is providing a similar functionality for R vectors.""" 829 error = False 830 try: 831 cls = conversion._R_RPY2_MAP[rtype] 832 except KeyError: 833 error = True 834 if not error and not issubclass(cls, SexpVector): 835 error = True 836 if error: 837 raise ValueError( 838 'Unable to build a vector from type "%s"' % RTYPES(rtype)) 839 return cls.from_iterable(iterable) 840 841 842class RRuntimeWarning(RuntimeWarning): 843 pass 844 845 846NULL = None 847MissingArg = _MissingArgType() 848NA_Character = None 849NA_Integer = None 850NA_Logical = None 851NA = None 852NA_Real = None 853NA_Complex = None 854 855 856def initr_simple() -> typing.Optional[int]: 857 """Initialize R's embedded C library.""" 858 with openrlib.rlock: 859 status = embedded._initr() 860 atexit.register(endr, 0) 861 _rinterface._register_external_symbols() 862 _post_initr_setup() 863 return status 864 865 866def initr_checkenv() -> typing.Optional[int]: 867 # Force the internal initialization flag if there is an environment 868 # variable that indicates that R was already initialized in the current 869 # process. 870 871 status = None 872 with openrlib.rlock: 873 if embedded.is_r_externally_initialized(): 874 embedded._setinitialized() 875 else: 876 status = embedded._initr() 877 embedded.set_python_process_info() 878 _rinterface._register_external_symbols() 879 _post_initr_setup() 880 return status 881 882 883initr = initr_checkenv 884 885 886def _update_R_ENC_PY(): 887 conversion._R_ENC_PY[openrlib.rlib.CE_UTF8] = 'utf-8' 888 889 l10n_info = tuple(baseenv['l10n_info']()) 890 if platform.system() == 'Windows': 891 val_latin1 = 'cp{:d}'.format(l10n_info[3][0]) 892 else: 893 val_latin1 = 'latin1' 894 conversion._R_ENC_PY[openrlib.rlib.CE_LATIN1] = val_latin1 895 896 if l10n_info[1]: 897 val_native = conversion._R_ENC_PY[openrlib.rlib.CE_UTF8] 898 else: 899 val_native = val_latin1 900 conversion._R_ENC_PY[openrlib.rlib.CE_NATIVE] = val_native 901 902 conversion._R_ENC_PY[openrlib.rlib.CE_ANY] = 'utf-8' 903 904 905def _post_initr_setup() -> None: 906 907 emptyenv.__sexp__ = _rinterface.SexpCapsule(openrlib.rlib.R_EmptyEnv) 908 baseenv.__sexp__ = _rinterface.SexpCapsule(openrlib.rlib.R_BaseEnv) 909 globalenv.__sexp__ = _rinterface.SexpCapsule(openrlib.rlib.R_GlobalEnv) 910 911 global NULL 912 NULL = NULLType() 913 914 MissingArg._sexpobject = _rinterface.UnmanagedSexpCapsule( 915 openrlib.rlib.R_MissingArg 916 ) 917 918 global NA_Character 919 na_values.NA_Character = sexp.NACharacterType() 920 NA_Character = na_values.NA_Character 921 922 global NA_Integer 923 na_values.NA_Integer = sexp.NAIntegerType(openrlib.rlib.R_NaInt) 924 NA_Integer = na_values.NA_Integer 925 926 global NA_Logical, NA 927 na_values.NA_Logical = sexp.NALogicalType(openrlib.rlib.R_NaInt) 928 NA_Logical = na_values.NA_Logical 929 NA = NA_Logical 930 931 global NA_Real 932 na_values.NA_Real = sexp.NARealType(openrlib.rlib.R_NaReal) 933 NA_Real = na_values.NA_Real 934 935 global NA_Complex 936 na_values.NA_Complex = sexp.NAComplexType( 937 _rinterface.ffi.new( 938 'Rcomplex *', 939 [openrlib.rlib.R_NaReal, openrlib.rlib.R_NaReal]) 940 ) 941 NA_Complex = na_values.NA_Complex 942 943 warn_about_thread = False 944 if threading.current_thread() is threading.main_thread(): 945 try: 946 signal.signal(signal.SIGINT, _sigint_handler) 947 except ValueError as ve: 948 if str(ve) == 'signal only works in main thread': 949 warn_about_thread = True 950 else: 951 raise ve 952 else: 953 warn_about_thread = True 954 if warn_about_thread: 955 warnings.warn( 956 textwrap.dedent( 957 """R is not initialized by the main thread. 958 Its taking over SIGINT cannot be reversed here, and as a 959 consequence the embedded R cannot be interrupted with Ctrl-C. 960 Consider (re)setting the signal handler of your choice from 961 the main thread.""" 962 ) 963 ) 964 965 _update_R_ENC_PY() 966 967 968def rternalize(function: typing.Callable) -> SexpClosure: 969 """ Make a Python function callable from R. 970 971 Takes an arbitrary Python function and wrap it in such a way that 972 it can be called from the R side. 973 974 :param function: A Python callable object. 975 :return: A wrapped R object that can be use like any other rpy2 976 object. 977 """ 978 979 assert callable(function) 980 rpy_fun = SexpExtPtr.from_pyobject(function) 981 # TODO: this is a hack. Find a better way. 982 template = parse(""" 983 function(...) { .External(".Python", foo, ...); 984 } 985 """) 986 template[0][2][1][2] = rpy_fun 987 # TODO: use lower-level eval ? 988 res = baseenv['eval'](template) 989 # TODO: hack to prevent the nested function from having its 990 # refcount down to zero when returning 991 res.__nested_sexp__ = rpy_fun.__sexp__ 992 return res 993 994 995def process_revents() -> None: 996 """Process R events. 997 998 Calling this function a regular interval can help ensure that 999 R is processing input events (e.g., resizing of a window with 1000 graphics).""" 1001 openrlib.rlib.rpy2_runHandlers(openrlib.rlib.R_InputHandlers) 1002