1"""
2R objects as Python objects.
3
4The module is structured around the singleton r of class R,
5that represents an embedded R.
6
7License: GPLv2+
8
9"""
10
11import array
12import contextlib
13import os
14from functools import partial
15import types
16import rpy2.rinterface as rinterface
17import rpy2.rlike.container as rlc
18
19from rpy2.robjects.robject import RObjectMixin, RObject
20import rpy2.robjects.functions
21from rpy2.robjects.environments import (Environment,
22                                        local_context)
23from rpy2.robjects.methods import methods_env
24from rpy2.robjects.methods import RS4
25
26from . import conversion
27from . import vectors
28from . import language
29
30from rpy2.rinterface import (Sexp,
31                             SexpVector,
32                             SexpClosure,
33                             SexpEnvironment,
34                             SexpS4,
35                             StrSexpVector,
36                             SexpExtPtr)
37
38from rpy2.robjects.functions import Function
39from rpy2.robjects.functions import SignatureTranslatedFunction
40
41
42_globalenv = rinterface.globalenv
43_reval = rinterface.baseenv['eval']
44
45BoolVector = vectors.BoolVector
46IntVector = vectors.IntVector
47FloatVector = vectors.FloatVector
48ComplexVector = vectors.ComplexVector
49StrVector = vectors.StrVector
50FactorVector = vectors.FactorVector
51Vector = vectors.Vector
52PairlistVector = vectors.PairlistVector
53ListVector = vectors.ListVector
54DateVector = vectors.DateVector
55POSIXct = vectors.POSIXct
56POSIXlt = vectors.POSIXlt
57Array = vectors.Array
58Matrix = vectors.Matrix
59DataFrame = vectors.DataFrame
60
61# Missing values.
62NA_Real = rinterface.NA_Real
63NA_Integer = rinterface.NA_Integer
64NA_Logical = rinterface.NA_Logical
65NA_Character = rinterface.NA_Character
66NA_Complex = rinterface.NA_Complex
67NULL = rinterface.NULL
68
69
70def reval(string, envir=_globalenv):
71    """ Evaluate a string as R code
72    - string: a string
73    - envir: an environment in which the environment should take place
74             (default: R's global environment)
75    """
76    p = rinterface.parse(string)
77    res = _reval(p, envir=envir)
78    return res
79
80
81default_converter = conversion.Converter('base empty converter')
82
83
84@default_converter.rpy2py.register(RObject)
85def _rpy2py_robject(obj):
86    return obj
87
88
89def _vector_matrix_array(obj, vector_cls, matrix_cls, array_cls):
90    # Should it be promoted to array or matrix ?
91    try:
92        dim = obj.do_slot("dim")
93        if len(dim) == 2:
94            return matrix_cls
95        else:
96            return array_cls
97    except Exception:
98        return vector_cls
99
100
101@default_converter.rpy2py.register(rinterface.IntSexpVector)
102def _convert_rpy2py_intvector(obj):
103    clsmap = conversion.converter.rpy2py_nc_name[rinterface.IntSexpVector]
104    cls = clsmap.find(obj.rclass)
105    return cls(obj)
106
107
108@default_converter.rpy2py.register(rinterface.FloatSexpVector)
109def _convert_rpy2py_floatvector(obj):
110    clsmap = conversion.converter.rpy2py_nc_name[rinterface.FloatSexpVector]
111    cls = clsmap.find(obj.rclass)
112    return cls(obj)
113
114
115@default_converter.rpy2py.register(rinterface.ComplexSexpVector)
116def _convert_rpy2py_complexvector(obj):
117    clsmap = conversion.converter.rpy2py_nc_name[rinterface.ComplexSexpVector]
118    cls = clsmap.find(obj.rclass)
119    return cls(obj)
120
121
122@default_converter.rpy2py.register(rinterface.BoolSexpVector)
123def _convert_rpy2py_boolvector(obj):
124    clsmap = conversion.converter.rpy2py_nc_name[rinterface.BoolSexpVector]
125    cls = clsmap.find(obj.rclass)
126    return cls(obj)
127
128
129@default_converter.rpy2py.register(rinterface.StrSexpVector)
130def _convert_rpy2py_strvector(obj):
131    cls = _vector_matrix_array(obj, vectors.StrVector,
132                               vectors.StrMatrix, vectors.StrArray)
133    return cls(obj)
134
135
136@default_converter.rpy2py.register(rinterface.ByteSexpVector)
137def _convert_rpy2py_bytevector(obj):
138    cls = _vector_matrix_array(obj, vectors.ByteVector,
139                               vectors.ByteMatrix, vectors.ByteArray)
140    return cls(obj)
141
142
143default_converter.rpy2py.register(rinterface.PairlistSexpVector, PairlistVector)
144
145@default_converter.rpy2py.register(rinterface.LangSexpVector)
146def _convert_rpy2py_langvector(obj):
147    if 'formula' in obj.rclass:
148        cls = Formula
149    else:
150        cls = language.LangVector
151    return cls(obj)
152
153
154TYPEORDER = {bool: (0, BoolVector),
155             int: (1, IntVector),
156             float: (2, FloatVector),
157             complex: (3, ComplexVector),
158             str: (4, StrVector)}
159
160
161def sequence_to_vector(lst):
162    curr_typeorder = -1
163    i = None
164    for i, elt in enumerate(lst):
165        cls = type(elt)
166        if cls in TYPEORDER:
167            if TYPEORDER[cls][0] > curr_typeorder:
168                curr_typeorder, curr_type = TYPEORDER[cls]
169        else:
170            raise ValueError('The element %i in the list has a type '
171                             'that cannot be handled.' % i)
172    if i is None:
173        raise ValueError('The parameter "lst" is an empty sequence. '
174                         'The type of the corresponding R vector cannot '
175                         'be determined.')
176    res = curr_type(lst)
177    return res
178
179
180@default_converter.py2rpy.register(rinterface._MissingArgType)
181def _py2rpy_missingargtype(obj):
182    return obj
183
184
185@default_converter.py2rpy.register(bool)
186def _py2rpy_bool(obj):
187    return obj
188
189
190@default_converter.py2rpy.register(int)
191def _py2rpy_int(obj):
192    return obj
193
194
195@default_converter.py2rpy.register(float)
196def _py2rpy_float(obj):
197    return obj
198
199
200@default_converter.py2rpy.register(bytes)
201def _py2rpy_bytes(obj):
202    return obj
203
204
205@default_converter.py2rpy.register(str)
206def _py2rpy_str(obj):
207    return obj
208
209
210@default_converter.rpy2py.register(SexpClosure)
211def _rpy2py_sexpclosure(obj):
212    return SignatureTranslatedFunction(obj)
213
214
215@default_converter.rpy2py.register(SexpEnvironment)
216def _rpy2py_sexpenvironment(obj):
217    return Environment(obj)
218
219
220@default_converter.rpy2py.register(rinterface.ListSexpVector)
221def _rpy2py_listsexp(obj):
222    clsmap = conversion.converter.rpy2py_nc_name[rinterface.ListSexpVector]
223    cls = clsmap.find(obj.rclass)
224    return cls(obj)
225
226
227@default_converter.rpy2py.register(SexpS4)
228def _rpy2py_sexps4(obj):
229    clsmap = conversion.converter.rpy2py_nc_name[SexpS4]
230    cls = clsmap.find(methods_env['extends'](obj.rclass))
231    return cls(obj)
232
233
234@default_converter.rpy2py.register(SexpExtPtr)
235def _rpy2py_sexpextptr(obj):
236    return obj
237
238
239@default_converter.rpy2py.register(object)
240def _rpy2py_object(obj):
241    return RObject(obj)
242
243
244@default_converter.rpy2py.register(type(NULL))
245def _rpy2py_null(obj):
246    return obj
247
248
249# TODO: delete ?
250def default_py2ri(o):
251    """ Convert an arbitrary Python object to a
252    :class:`rpy2.rinterface.Sexp` object.
253    Creates an R object with the content of the Python object,
254    wich means data copying.
255    :param o: object
256    :rtype: :class:`rpy2.rinterface.Sexp` (and subclasses)
257    """
258    pass
259
260
261@default_converter.py2rpy.register(RObject)
262def _py2rpy_robject(obj):
263    return rinterface.Sexp(obj)
264
265
266@default_converter.py2rpy.register(Sexp)
267def _py2rpy_sexp(obj):
268    return obj
269
270
271@default_converter.py2rpy.register(array.array)
272def _py2rpy_array(obj):
273    if obj.typecode in ('h', 'H', 'i', 'I'):
274        res = IntVector(obj)
275    elif obj.typecode in ('f', 'd'):
276        res = FloatVector(obj)
277    else:
278        raise(
279            ValueError('Nothing can be done for this array '
280                       'type at the moment.')
281        )
282    return res
283
284
285default_converter.py2rpy.register(int,
286                                  lambda x: x)
287
288
289@default_converter.py2rpy.register(list)
290def _py2rpy_list(obj):
291    return vectors.ListVector(
292        rinterface.ListSexpVector(
293            [conversion.py2rpy(x) for x in obj]
294        )
295    )
296
297
298@default_converter.py2rpy.register(rlc.TaggedList)
299def _py2rpy_taggedlist(obj):
300    res = vectors.ListVector(
301        rinterface.ListSexpVector([conversion.py2rpy(x) for x in obj])
302    )
303    res.do_slot_assign('names', rinterface.StrSexpVector(obj.tags))
304    return res
305
306
307@default_converter.py2rpy.register(complex)
308def _py2rpy_complex(obj):
309    return obj
310
311
312@default_converter.py2rpy.register(types.FunctionType)
313def _function_to_rpy(func):
314    def wrap(*args):
315        res = func(*args)
316        res = conversion.py2ro(res)
317        return res
318    rfunc = rinterface.rternalize(wrap)
319    return conversion.rpy2py(rfunc)
320
321
322@default_converter.rpy2py.register(object)
323def _(obj):
324    return obj
325
326
327default_converter._rpy2py_nc_map.update(
328    {
329        rinterface.SexpS4: conversion.NameClassMap(RS4),
330        rinterface.IntSexpVector: conversion.NameClassMap(
331            lambda obj: _vector_matrix_array(obj, vectors.IntVector,
332                                             vectors.IntMatrix, vectors.IntArray)(obj),
333            {'factor': FactorVector}),
334        rinterface.FloatSexpVector: conversion.NameClassMap(
335            lambda obj: _vector_matrix_array(obj, vectors.FloatVector,
336                                             vectors.FloatMatrix, vectors.FloatArray)(obj),
337            {'Date': DateVector,
338             'POSIXct': POSIXct}),
339        rinterface.BoolSexpVector: conversion.NameClassMap(
340            lambda obj: _vector_matrix_array(obj, vectors.BoolVector,
341                                             vectors.BoolMatrix, vectors.BoolArray)(obj)
342        ),
343        rinterface.ByteSexpVector: conversion.NameClassMap(
344            lambda obj: _vector_matrix_array(obj, vectors.ByteVector,
345                                             vectors.ByteMatrix, vectors.ByteArray)(obj)
346        ),
347        rinterface.StrSexpVector: conversion.NameClassMap(
348            lambda obj: _vector_matrix_array(obj, vectors.StrVector,
349                                             vectors.StrMatrix, vectors.StrArray)(obj)
350        ),
351        rinterface.ComplexSexpVector: conversion.NameClassMap(
352            lambda obj: _vector_matrix_array(obj, vectors.ComplexVector,
353                                             vectors.ComplexMatrix, vectors.ComplexArray)(obj)
354        ),
355        rinterface.ListSexpVector: conversion.NameClassMap(
356            ListVector,
357            {'data.frame': DataFrame}),
358        rinterface.SexpEnvironment: conversion.NameClassMap(Environment)
359    }
360)
361
362class Formula(RObjectMixin, rinterface.Sexp):
363
364    def __init__(self, formula, environment=_globalenv):
365        if isinstance(formula, str):
366            inpackage = rinterface.baseenv["::"]
367            asformula = inpackage(rinterface.StrSexpVector(['stats', ]),
368                                  rinterface.StrSexpVector(['as.formula', ]))
369            formula = rinterface.StrSexpVector([formula, ])
370            robj = asformula(formula,
371                             env=environment)
372        else:
373            robj = formula
374        super(Formula, self).__init__(robj)
375
376    def getenvironment(self):
377        """ Get the environment in which the formula is finding its symbols."""
378        res = self.do_slot(".Environment")
379        res = conversion.rpy2py(res)
380        return res
381
382    def setenvironment(self, val):
383        """ Set the environment in which a formula will find its symbols."""
384        if not isinstance(val, rinterface.SexpEnvironment):
385            raise TypeError('The environment must be an instance of'
386                            ' rpy2.rinterface.Sexp.environment')
387        self.do_slot_assign('.Environment', val)
388
389    environment = property(getenvironment, setenvironment, None,
390                           'R environment in which the formula will look for '
391                           'its variables.')
392
393
394class R(object):
395    """
396    Singleton representing the embedded R running.
397    """
398    _instance = None
399
400    def __new__(cls):
401        if cls._instance is None:
402            rinterface.initr_simple()
403            cls._instance = object.__new__(cls)
404        return cls._instance
405
406    def __getattribute__(self, attr):
407        try:
408            return super(R, self).__getattribute__(attr)
409        except AttributeError as ae:
410            orig_ae = str(ae)
411
412        try:
413            return self.__getitem__(attr)
414        except LookupError:
415            raise AttributeError(orig_ae)
416
417    def __getitem__(self, item):
418        res = _globalenv.find(item)
419        res = conversion.rpy2py(res)
420        if hasattr(res, '__rname__'):
421            res.__rname__ = item
422        return res
423
424    # TODO: check that this is properly working
425    def __cleanup__(self):
426        rinterface.embedded.endr(0)
427        del(self)
428
429    def __str__(self):
430        version = self['version']
431        s = [super(R, self).__str__()]
432        s.extend('%s: %s' % (n, val[0])
433                 for n, val in zip(version.names, version))
434        return os.linesep.join(s)
435
436    def __call__(self, string):
437        p = rinterface.parse(string)
438        res = self.eval(p)
439        return conversion.rpy2py(res)
440
441
442r = R()
443rl = language.LangVector.from_string
444
445conversion.set_conversion(default_converter)
446
447globalenv = conversion.converter.rpy2py(_globalenv)
448baseenv = conversion.converter.rpy2py(rinterface.baseenv)
449emptyenv = conversion.converter.rpy2py(rinterface.emptyenv)
450