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