rpy2-3.2.6/0000755000175000017500000000000013615572523013575 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/0000755000175000017500000000000013615572523014471 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/_rinterface_cffi_build.py0000644000175000017500000001342413615570032021467 0ustar laurentlaurent00000000000000import cffi import os import re import sys import rpy2.situation from rpy2.rinterface_lib import ffi_proxy ifdef_pat = re.compile('^#ifdef +([^ ]+) *$') define_pat = re.compile('^#define +([^ ]+) +([^ ]+) *$') cffi_source_pat = re.compile( '^/[*] cffi_source-begin [*]/.+?/[*] cffi_source-end [*]/', flags=re.MULTILINE | re.DOTALL) def define_rlen_kind(ffibuilder, definitions): if ffibuilder.sizeof('size_t') > 4: # The following was defined in the first cffi port, # and they in the C-extension version. They should # be ported to the header. # LONG_VECTOR_SUPPORT = True # R_XLEN_T_MAX = 4503599627370496 # R_SHORT_LEN_MAX = 2147483647 definitions['RPY2_RLEN_LONG'] = True else: definitions['RPY2_RLEN_SHORT'] = True def define_osname(definitions): if os.name == 'nt': definitions['OSNAME_NT'] = True def parse(iterrows, rownum, definitions, until=None): res = [] for row_i, row in enumerate(iter(iterrows), rownum): if until and row.startswith(until): break m = ifdef_pat.match(row) if m: defined = m.groups()[0].strip() block = parse(iterrows, row_i, definitions, until='#endif') if defined in definitions: res.extend(block) continue m = define_pat.match(row) if m: alias, value = m.groups() definitions[alias] = value.strip() continue for k, v in definitions.items(): if isinstance(v, str): row = row.replace(k, v) res.append(row) return res def create_cdef(definitions, header_filename): cdef = [] with open( os.path.join( os.path.dirname(os.path.realpath(__file__)), 'rinterface_lib', header_filename) ) as fh: iterrows = iter(fh) cdef.extend(parse(iterrows, 0, definitions)) return ''.join(cdef) def read_header(header_filename): with open( os.path.join( os.path.dirname(os.path.realpath(__file__)), 'rinterface_lib', header_filename) ) as fh: cdef = fh.read() return cdef def createbuilder_abi(): ffibuilder = cffi.FFI() cdef = [] definitions = {} define_rlen_kind(ffibuilder, definitions) define_osname(definitions) cdef = create_cdef(definitions, 'R_API.h') ffibuilder.set_source('_rinterface_cffi_abi', None) ffibuilder.cdef(cdef) return ffibuilder def createbuilder_api(): header_filename = 'R_API.h' ffibuilder = cffi.FFI() definitions = {} define_rlen_kind(ffibuilder, definitions) define_osname(definitions) cdef = create_cdef(definitions, header_filename) header_eventloop = read_header('R_API_eventloop.h') r_home = rpy2.situation.get_r_home() if r_home is None: sys.exit('Error: rpy2 in API mode cannot be built without R in ' 'the PATH or R_HOME defined. Correct this or force ' 'ABI mode-only by defining the environment variable ' 'RPY2_CFFI_MODE=ABI') c_ext = rpy2.situation.CExtensionOptions() c_ext.add_lib( *rpy2.situation.get_r_flags(r_home, '--ldflags') ) c_ext.add_include( *rpy2.situation.get_r_flags(r_home, '--cppflags') ) define_macros = [('RPY2_RLEN_LONG' if 'RPY2_RLEN_LONG' in definitions else 'RPY2_RLEN_SHORT', True)] ffibuilder.set_source( '_rinterface_cffi_api', f""" # include "{header_filename}" # include "R_API_eventloop.h" void rpy2_runHandlers(InputHandler *handlers) {{ R_runHandlers(handlers, R_checkActivity(0, 1)); }}; """, libraries=c_ext.libraries, library_dirs=c_ext.library_dirs, # If we were using the R headers, we would use # include_dirs=c_ext.include_dirs. include_dirs=['rpy2/rinterface_lib/'], define_macros=define_macros, extra_compile_args=c_ext.extra_compile_args, extra_link_args=c_ext.extra_link_args) callback_defns_api = os.linesep.join( x.extern_python_def for x in [ffi_proxy._capsule_finalizer_def, ffi_proxy._evaluate_in_r_def, ffi_proxy._consoleflush_def, ffi_proxy._consoleread_def, ffi_proxy._consolereset_def, ffi_proxy._consolewrite_def, ffi_proxy._consolewrite_ex_def, ffi_proxy._showmessage_def, ffi_proxy._choosefile_def, ffi_proxy._cleanup_def, ffi_proxy._showfiles_def, ffi_proxy._processevents_def, ffi_proxy._busy_def, ffi_proxy._callback_def, ffi_proxy._yesnocancel_def, ffi_proxy._parsevector_wrap_def, ffi_proxy._handler_def]) cdef = (create_cdef(definitions, header_filename) + callback_defns_api) ffibuilder.cdef(cdef) ffibuilder.cdef(cffi_source_pat.sub('', header_eventloop)) ffibuilder.cdef('void rpy2_runHandlers(InputHandler *handlers);') return ffibuilder ffibuilder_abi = createbuilder_abi() ffibuilder_api = createbuilder_api() if __name__ == '__main__': # This sort of redundant with setup.py defining cffi_modules, # but at least both use rpy2.situation.get_ffi_mode(). cffi_mode = rpy2.situation.get_ffi_mode() if cffi_mode in (rpy2.situation.CFFI_MODE.ABI, rpy2.situation.CFFI_MODE.BOTH): ffibuilder_abi.compile(verbose=True) if cffi_mode in (rpy2.situation.CFFI_MODE.API, rpy2.situation.CFFI_MODE.BOTH): ffibuilder_api.compile(verbose=True) rpy2-3.2.6/rpy2/rlike/0000755000175000017500000000000013615572523015577 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/rlike/__init__.py0000644000175000017500000000000013576515767017713 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/rlike/functional.py0000644000175000017500000000173113576515767020332 0ustar laurentlaurent00000000000000def tapply(seq, tag, fun): """ Apply the function `fun` to the items in `seq`, grouped by the tags defined in `tag`. :param seq: sequence :param tag: any sequence of tags :param fun: function :rtype: list """ if len(seq) != len(tag): raise ValueError("seq and tag should have the same length.") tag_grp = {} for i, t in enumerate(tag): try: tag_grp[t].append(i) except LookupError: tag_grp[t] = [i, ] res = [(tag, fun([seq[i] for i in ti])) for tag, ti in tag_grp.items()] return res def listify(fun): """ Decorator to make a function apply to each item in a sequence, and return a list. """ def f(seq): res = [fun(x) for x in seq] return res return f def iterify(fun): """ Decorator to make a function apply to each item in a sequence, and return an iterator. """ def f(seq): for x in seq: yield fun(x) return f rpy2-3.2.6/rpy2/rlike/container.py0000644000175000017500000002031113576515767020145 0ustar laurentlaurent00000000000000import rpy2.rlike.indexing as rli class OrdDict(dict): """ Implements the Ordered Dict API defined in PEP 372. When `odict` becomes part of collections, this class should inherit from it rather than from `dict`. This class differs a little from the Ordered Dict proposed in PEP 372 by the fact that: not all elements have to be named. None as a key value means an absence of name for the element. """ def __init__(self, c=[]): if isinstance(c, TaggedList) or isinstance(c, OrdDict): c = c.items() elif isinstance(c, dict): # FIXME: allow instance from OrdDict ? raise TypeError('A regular dictionnary does not ' + 'conserve the order of its keys.') super(OrdDict, self).__init__() self.__l = [] for k, v in c: self[k] = v def __copy__(self): cp = OrdDict(c=tuple(self.items())) return cp def __cmp__(self, o): raise(NotImplementedError("Not yet implemented.")) def __eq__(self, o): raise(NotImplementedError("Not yet implemented.")) def __getitem__(self, key): if key is None: raise ValueError("Unnamed items cannot be retrieved by key.") i = super(OrdDict, self).__getitem__(key) return self.__l[i][1] def __iter__(self): seq = self.__l for e in seq: k = e[0] if k is None: continue else: yield k def __len__(self): return len(self.__l) def __ne__(self, o): raise(NotImplementedError('Not yet implemented.')) def __repr__(self): s = ['o{', ] for k, v in self.items(): s.append("'%s': %s, " % (str(k), str(v))) s.append('}') return ''.join(s) def __reversed__(self): raise(NotImplementedError("Not yet implemented.")) def __setitem__(self, key, value): """ Replace the element if the key is known, and conserve its rank in the list, or append it if unknown. """ if key is None: self.__l.append((key, value)) return if key in self: i = self.index(key) self.__l[i] = (key, value) else: self.__l.append((key, value)) super(OrdDict, self).__setitem__(key, len(self.__l)-1) def byindex(self, i): """ Fetch a value by index (rank), rather than by key.""" return self.__l[i] def index(self, k): """ Return the index (rank) for the key 'k' """ return super(OrdDict, self).__getitem__(k) def get(self, k, d=None): """ OD.get(k[,d]) -> OD[k] if k in OD, else d. d defaults to None """ try: res = self[k] except KeyError: res = d return res def items(self): """ OD.items() -> an iterator over the (key, value) items of D """ return iter(self.__l) def keys(self): """ """ return tuple([x[0] for x in self.__l]) def reverse(self): """ Reverse the order of the elements in-place (no copy).""" seq = self.__l n = len(self.__l) for i in range(n//2): tmp = seq[i] seq[i] = seq[n-i-1] kv = seq[i] if kv is not None: super(OrdDict, self).__setitem__(kv[0], i) seq[n-i-1] = tmp kv = tmp if kv is not None: super(OrdDict, self).__setitem__(kv[0], n-i-1) def sort(self, cmp=None, key=None, reverse=False): raise NotImplementedError("Not yet implemented.") class TaggedList(list): """ A list for which each item has a 'tag'. :param l: list :param tag: optional sequence of tags """ def __add__(self, tl): try: tags = tl.tags except AttributeError: raise ValueError('Can only concatenate TaggedLists.') res = TaggedList(list(self) + list(tl), tags=self.tags + tags) return res def __delitem__(self, y): super(TaggedList, self).__delitem__(y) self.__tags.__delitem__(y) def __delslice__(self, i, j): super(TaggedList, self).__delslice__(i, j) self.__tags.__delslice__(i, j) def __iadd__(self, y): super(TaggedList, self).__iadd__(y) if isinstance(y, TaggedList): self.__tags.__iadd__(y.tags) else: self.__tags.__iadd__([None, ] * len(y)) return self def __imul__(self, y): restags = self.__tags.__imul__(y) resitems = super(TaggedList, self).__imul__(y) return self @staticmethod def from_items(tagval): res = TaggedList([]) for k, v in tagval.items(): res.append(v, tag=k) return res def __init__(self, seq, tags=None): super(TaggedList, self).__init__(seq) if tags is None: tags = [None, ] * len(seq) if len(tags) != len(seq): raise ValueError("There must be as many tags as seq") self.__tags = list(tags) def __setslice__(self, i, j, y): super(TaggedList, self).__setslice__(i, j, y) # TODO: handle TaggedList ? # self.__tags.__setslice__(i, j, [None, ]) def append(self, obj, tag=None): """ Append an object to the list :param obj: object :param tag: object """ super(TaggedList, self).append(obj) self.__tags.append(tag) def extend(self, iterable): """ Extend the list with an iterable object. :param iterable: iterable object """ if isinstance(iterable, TaggedList): itertags = iterable.itertags() else: itertags = [None, ] * len(iterable) for tag, item in zip(itertags, iterable): self.append(item, tag=tag) def insert(self, index, obj, tag=None): """ Insert an object in the list :param index: integer :param obj: object :param tag: object """ super(TaggedList, self).insert(index, obj) self.__tags.insert(index, tag) def iterontag(self, tag): """ iterate on items marked with one given tag. :param tag: object """ i = 0 for onetag in self.__tags: if tag == onetag: yield self[i] i += 1 def items(self): """ OD.items() -> an iterator over the (key, value) items of D """ for tag, item in zip(self.__tags, self): yield (tag, item) def itertags(self): """ iterate on tags. :rtype: iterator """ for tag in self.__tags: yield tag def pop(self, index=None): """ Pop the item at a given index out of the list :param index: integer """ if index is None: index = len(self) - 1 res = super(TaggedList, self).pop(index) self.__tags.pop(index) return res def remove(self, value): """ Remove a given value from the list. :param value: object """ found = False for i in range(len(self)): if self[i] == value: found = True break if found: self.pop(i) def reverse(self): """ Reverse the order of the elements in the list. """ super(TaggedList, self).reverse() self.__tags.reverse() def sort(self, reverse=False): """ Sort in place """ o = rli.order(self, reverse=reverse) super(TaggedList, self).sort(reverse=reverse) self.__tags = [self.__tags[i] for i in o] def __get_tags(self): return tuple(self.__tags) def __set_tags(self, tags): if len(tags) == len(self.__tags): self.__tags = tuple(tags) else: raise ValueError('The new list of tags should have the ' 'same length as the old one.') tags = property(__get_tags, __set_tags) def settag(self, i, t): """ Set tag 't' for item 'i'. :param i: integer (index) :param t: object (tag) """ self.__tags[i] = t rpy2-3.2.6/rpy2/rlike/indexing.py0000644000175000017500000000055313576515767017776 0ustar laurentlaurent00000000000000def default_key(x): """ Default comparison function.""" return x def order(seq, key=default_key, reverse=False): """ Return the order in which to take the items to obtained a sorted sequence.""" o = list(range(len(seq))) def wrap_key(x): x = seq[x] return key(x) o.sort(key=wrap_key, reverse=reverse) return o rpy2-3.2.6/rpy2/tests/0000755000175000017500000000000013615572523015633 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/utils.py0000644000175000017500000000070113615570032017334 0ustar laurentlaurent00000000000000from contextlib import contextmanager @contextmanager def obj_in_module(module, name, obj): backup_obj = getattr(module, name, None) setattr(module, name, obj) try: yield finally: if backup_obj: setattr(module, name, backup_obj) def assert_equal_sequence(x, y): assert type(x) is type(y) assert len(x) == len(y) assert all(x_e == y_e for x_e, y_e in zip(x, y)) def noconsole(x): pass rpy2-3.2.6/rpy2/tests/rlike/0000755000175000017500000000000013615572523016741 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/rlike/test_container.py0000644000175000017500000001703313576515767022355 0ustar laurentlaurent00000000000000import pytest import rpy2.rlike.container as rlc class TestOrdDict(object): def test_new(self): nl = rlc.OrdDict() x = (('a', 123), ('b', 456), ('c', 789)) nl = rlc.OrdDict(x) def test_new_invalid(self): with pytest.raises(TypeError): rlc.OrdDict({}) @pytest.mark.parametrize('methodname,args', (('__cmp__', [None]), ('__eq__', [None]), ('__ne__', [None]), ('__reversed__', []), ('sort', []))) def test_notimplemented(self, methodname, args): nl = rlc.OrdDict() with pytest.raises(NotImplementedError): getattr(nl, methodname)(*args) def test_repr(self): x = (('a', 123), ('b', 456), ('c', 789)) nl = rlc.OrdDict(x) assert isinstance(repr(nl), str) def test_iter(self): x = (('a', 123), ('b', 456), ('c', 789)) nl = rlc.OrdDict(x) for a, b in zip(nl, x): assert a == b[0] def test_len(self): x = rlc.OrdDict() assert len(x) == 0 x['a'] = 2 x['b'] = 1 assert len(x) == 2 def test_getsetitem(self): x = rlc.OrdDict() x['a'] = 1 assert len(x) == 1 assert x['a'] == 1 assert x.index('a') == 0 x['a'] = 2 assert len(x) == 1 assert x['a'] == 2 assert x.index('a') == 0 x['b'] = 1 assert len(x) == 2 assert x['b'] == 1 assert x.index('b') == 1 def test_get(self): x = rlc.OrdDict() x['a'] = 1 assert x.get('a') == 1 assert x.get('b') is None assert x.get('b', 2) == 2 def test_keys(self): x = rlc.OrdDict() word = 'abcdef' for i,k in enumerate(word): x[k] = i for i,k in enumerate(x.keys()): assert word[i] == k def test_getsetitemwithnone(self): x = rlc.OrdDict() x['a'] = 1 x[None] = 2 assert len(x) == 2 x['b'] = 5 assert len(x) == 3 assert x['a'] == 1 assert x['b'] == 5 assert x.index('a') == 0 assert x.index('b') == 2 def test_reverse(self): x = rlc.OrdDict() x['a'] = 3 x['b'] = 2 x['c'] = 1 x.reverse() assert x['c'] == 1 assert x.index('c') == 0 assert x['b'] == 2 assert x.index('b') == 1 assert x['a'] == 3 assert x.index('a') == 2 def test_items(self): args = (('a', 5), ('b', 4), ('c', 3), ('d', 2), ('e', 1)) x = rlc.OrdDict(args) it = x.items() for ki, ko in zip(args, it): assert ki[0] == ko[0] assert ki[1] == ko[1] class TestTaggedList(object): def test__add__(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) tl = tl + tl assert len(tl) == 6 assert tl.tags == ('a', 'b', 'c', 'a', 'b', 'c') assert tuple(tl) == (1,2,3,1,2,3) def test__delitem__(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) assert len(tl) == 3 del tl[1] assert len(tl) == 2 assert tl.tags == ('a', 'c') assert tuple(tl) == (1, 3) def test__delslice__(self): tl = rlc.TaggedList((1,2,3,4), tags=('a', 'b', 'c', 'd')) del tl[1:3] assert len(tl) == 2 assert tl.tags == ('a', 'd') assert tuple(tl) == (1, 4) def test__iadd__(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) tl += tl assert len(tl) == 6 assert tl.tags == ('a', 'b', 'c', 'a', 'b', 'c') assert tuple(tl) == (1,2,3,1,2,3) def test__imul__(self): tl = rlc.TaggedList((1,2), tags=('a', 'b')) tl *= 3 assert len(tl) == 6 assert tl.tags == ('a', 'b', 'a', 'b', 'a', 'b') assert tuple(tl) == (1,2,1,2,1,2) def test__init__(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) with pytest.raises(ValueError): rlc.TaggedList((1,2,3), tags = ('b', 'c')) def test__setslice__(self): tl = rlc.TaggedList((1,2,3,4), tags=('a', 'b', 'c', 'd')) tl[1:3] = [5, 6] assert len(tl) == 4 assert tl.tags == ('a', 'b', 'c', 'd') assert tuple(tl) == (1, 5, 6, 4) def test_append(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) assert len(tl) == 3 tl.append(4, tag='a') assert len(tl) == 4 assert tl[3] == 4 assert tl.tags == ('a', 'b', 'c', 'a') def test_extend(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) tl.extend([4, 5]) assert tuple(tl.itertags()) == ('a', 'b', 'c', None, None) assert tuple(tl) == (1, 2, 3, 4, 5) def test_insert(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) tl.insert(1, 4, tag = 'd') assert tuple(tl.itertags()) == ('a', 'd', 'b', 'c') assert tuple(tl) == (1, 4, 2, 3) def test_items(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) assert tuple(tl.items()) == (('a', 1), ('b', 2), ('c', 3)) def test_iterontag(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'a')) assert tuple(tl.iterontag('a')) == (1, 3) def test_itertags(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) assert tuple(tl.itertags()) == ('a', 'b', 'c') def test_pop(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) assert len(tl) == 3 elt = tl.pop() assert elt == 3 assert len(tl) == 2 assert tl.tags == ('a', 'b') assert tuple(tl) == (1, 2) elt = tl.pop(0) assert elt == 1 assert len(tl) == 1 assert tl.tags == ('b', ) def test_remove(self): tl = rlc.TaggedList((1,2,3), tags=('a', 'b', 'c')) assert len(tl) == 3 tl.remove(2) assert len(tl) == 2 assert tl.tags == ('a', 'c') assert tuple(tl) == (1, 3) def test_reverse(self): tn = ['a', 'b', 'c'] tv = [1,2,3] tl = rlc.TaggedList(tv, tags = tn) tl.reverse() assert len(tl) == 3 assert tl.tags == ('c', 'b', 'a') assert tuple(tl) == (3, 2, 1) def test_sort(self): tn = ['a', 'c', 'b'] tv = [1,3,2] tl = rlc.TaggedList(tv, tags = tn) tl.sort() assert tl.tags == ('a', 'b', 'c') assert tuple(tl) == (1, 2, 3) def test_tags(self): tn = ['a', 'b', 'c'] tv = [1,2,3] tl = rlc.TaggedList(tv, tags = tn) tags = tl.tags assert isinstance(tags, tuple) is True assert tags == ('a', 'b', 'c') tn = ['d', 'e', 'f'] tl.tags = tn assert isinstance(tags, tuple) is True assert tl.tags == tuple(tn) def test_settag(self): tn = ['a', 'b', 'c'] tv = [1,2,3] tl = rlc.TaggedList(tv, tags = tn) tl.settag(1, 'z') assert tl.tags == ('a', 'z', 'c') def test_from_items(self): od = rlc.OrdDict( (('a', 1), ('b', 2), ('c', 3)) ) tl = rlc.TaggedList.from_items(od) assert tl.tags == ('a', 'b', 'c') assert tuple(tl) == (1, 2, 3) tl = rlc.TaggedList.from_items({'a':1, 'b':2, 'c':3}) assert set(tl.tags) == set(('a', 'b', 'c')) assert set(tuple(tl)) == set((1, 2, 3)) rpy2-3.2.6/rpy2/tests/rlike/test_functional.py0000644000175000017500000000117613576515767022536 0ustar laurentlaurent00000000000000import pytest import rpy2.rlike.functional as rlf def test_tapply_sumbystring(): seq = ( 1, 2, 3, 4, 5, 6) tags = ('a', 'b', 'a', 'c', 'b', 'a') expected = {'a': 1+3+6, 'b': 2+5, 'c': 4} res = rlf.tapply(seq, tags, sum) for k, v in res: assert expected[k] == v @pytest.mark.parametrize('subject_fun', [rlf.iterify, rlf.listify]) def test_simplefunction(subject_fun): def f(x): return x ** 2 f_iter = subject_fun(f) seq = (1, 2, 3) res = f_iter(seq) for va, vb in zip(seq, res): assert va ** 2 == vb rpy2-3.2.6/rpy2/tests/rlike/test_indexing.py0000644000175000017500000000033213576515767022172 0ustar laurentlaurent00000000000000import pytest import rpy2.rlike.indexing as rfi def test_order(): seq = (2, 1, 5, 3, 4) expected = (1, 2, 3, 4, 5) res = rfi.order(seq) for va, vb in zip(expected, res): assert seq[vb] == va rpy2-3.2.6/rpy2/tests/robjects/0000755000175000017500000000000013615572523017446 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/robjects/test_formula.py0000644000175000017500000000133213576515767022540 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface def test_init(): fml = robjects.Formula('y ~ x') assert fml.rclass[0] == 'formula' def test_getenvironment(): fml = robjects.Formula('y ~ x') env = fml.getenvironment() assert env.rclass[0] == 'environment' def test_setenvironment(): fml = robjects.Formula('y ~ x') newenv = robjects.baseenv['new.env']() env = fml.getenvironment() assert not newenv.rsame(env) fml.setenvironment(newenv) env = fml.getenvironment() assert newenv.rsame(env) def test_setenvironment_error(): fml = robjects.Formula('y ~ x') with pytest.raises(TypeError): fml.setenvironment(rinterface.NA_Logical) rpy2-3.2.6/rpy2/tests/robjects/test_conversion.py0000644000175000017500000000571713615570032023247 0ustar laurentlaurent00000000000000import array import pytest import rpy2.rinterface_lib.sexp from rpy2 import rinterface from rpy2 import robjects def test_mapperR2Python_string(): sexp = rinterface.globalenv.find('letters') ob = robjects.default_converter.rpy2py(sexp) assert isinstance(ob, robjects.Vector) def test_mapperR2Python_boolean(): sexp = rinterface.globalenv.find('T') ob = robjects.default_converter.rpy2py(sexp) assert isinstance(ob, robjects.Vector) def test_mapperR2Python_function(): sexp = rinterface.globalenv.find('plot') ob = robjects.default_converter.rpy2py(sexp) assert isinstance(ob, robjects.Function) def test_mapperR2Python_environment(): sexp = rinterface.globalenv.find('.GlobalEnv') assert isinstance(robjects.default_converter.rpy2py(sexp), robjects.Environment) def test_mapperR2Python_s4(): robjects.r('setClass("A", representation(x="integer"))') classname = rinterface.StrSexpVector(['A', ]) one = rinterface.IntSexpVector([1, ]) sexp = rinterface.globalenv.find('new')(classname, x=one) assert isinstance(robjects.default_converter.rpy2py(sexp), robjects.RS4) @pytest.mark.parametrize('value,cls', [ (1, int), (True, bool), (b'houba', bytes), (1.0, float), (1.0 + 2j, complex) ]) def test_py2ro_mappedtype(value, cls): pyobj = value assert isinstance(pyobj, cls) rob = robjects.default_converter.py2rpy(pyobj) assert isinstance(rob, cls) def test_mapperPy2R_taggedlist(): py = robjects.rlc.TaggedList(('a', 'b'), tags=('foo', 'bar')) robj = robjects.default_converter.py2rpy(py) assert isinstance(robj, robjects.Vector) assert len(robj) == 2 assert tuple(robj.names) == ('foo', 'bar') def test_mapperPy2R_function(): func = lambda x: x rob = robjects.default_converter.py2rpy(func) assert isinstance(rob, robjects.SignatureTranslatedFunction) assert rob.typeof == rinterface.RTYPES.CLOSXP @pytest.mark.parametrize('ctype', ['h', 'H', 'i', 'I']) def test_mapperpy2rpy_int_array(ctype): a = array.array(ctype, range(10)) rob = robjects.default_converter.py2rpy(a) assert isinstance(rob, robjects.vectors.IntSexpVector) assert isinstance(rob, robjects.vectors.IntVector) assert rob.typeof == rinterface.RTYPES.INTSXP @pytest.mark.parametrize('ctype', ['d', 'f']) def test_mapperpy2rpy_float_array(ctype): a = array.array(ctype, (1.1, 2.2, 3.3)) rob = robjects.default_converter.py2rpy(a) assert isinstance(rob, robjects.vectors.FloatSexpVector) assert isinstance(rob, robjects.vectors.FloatVector) assert rob.typeof == rinterface.RTYPES.REALSXP def noconversion(): robj_res = robjects.baseenv['pi'] assert isinstance(robj_res, robjects.RObject) rint_res = robject.conversion.noconversion(robj_res) assert isinstance(rint_res, rpy2.rinterface_lib.sexp.Sexp) rpy2-3.2.6/rpy2/tests/robjects/test_pandas_conversions.py0000644000175000017500000003643313576515767025003 0ustar laurentlaurent00000000000000import math from collections import OrderedDict from datetime import datetime import pytest from rpy2 import rinterface from rpy2 import robjects from rpy2.robjects import vectors from rpy2.robjects import conversion has_pandas = True try: import pandas import numpy has_pandas = True except: has_pandas = False if has_pandas: import rpy2.robjects.pandas2ri as rpyp from rpy2.robjects import default_converter from rpy2.robjects.conversion import localconverter @pytest.mark.skipif(not has_pandas, reason='Package pandas is not installed.') class TestPandasConversions(object): def testActivate(self): #FIXME: is the following still making sense ? assert rpyp.py2rpy != robjects.conversion.py2rpy l = len(robjects.conversion.py2rpy.registry) k = set(robjects.conversion.py2rpy.registry.keys()) rpyp.activate() assert len(conversion.py2rpy.registry) > l rpyp.deactivate() assert len(conversion.py2rpy.registry) == l assert set(conversion.py2rpy.registry.keys()) == k def testActivateTwice(self): #FIXME: is the following still making sense ? assert rpyp.py2rpy != robjects.conversion.py2rpy l = len(robjects.conversion.py2rpy.registry) k = set(robjects.conversion.py2rpy.registry.keys()) rpyp.activate() rpyp.deactivate() rpyp.activate() assert len(conversion.py2rpy.registry) > l rpyp.deactivate() assert len(conversion.py2rpy.registry) == l assert set(conversion.py2rpy.registry.keys()) == k def test_dataframe(self): # Content for test data frame l = ( ('b', numpy.array([True, False, True], dtype=numpy.bool_)), ('i', numpy.array([1, 2, 3], dtype='i')), ('f', numpy.array([1, 2, 3], dtype='f')), # ('s', numpy.array([b'b', b'c', b'd'], dtype='S1')), ('u', numpy.array([u'a', u'b', u'c'], dtype='U')), ('dates', [datetime(2012, 5, 2), datetime(2012, 6, 3), datetime(2012, 7, 1)]) ) od = OrderedDict(l) # Pandas data frame pd_df = pandas.core.frame.DataFrame(od) # Convert to R with localconverter(default_converter + rpyp.converter) as cv: rp_df = robjects.conversion.py2rpy(pd_df) assert pd_df.shape[0] == rp_df.nrow assert pd_df.shape[1] == rp_df.ncol # assert tuple(rp_df.rx2('s')) == (b'b', b'c', b'd') assert tuple(rp_df.rx2('u')) == ('a', 'b', 'c') def test_series(self): Series = pandas.core.series.Series s = Series(numpy.random.randn(5), index=['a', 'b', 'c', 'd', 'e']) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.FloatSexpVector) @pytest.mark.parametrize('dtype', ('i', numpy.int32 if has_pandas else None, numpy.int64 if has_pandas else None, pandas.Int32Dtype if has_pandas else None, pandas.Int64Dtype if has_pandas else None)) def test_series_int(self, dtype): Series = pandas.core.series.Series s = Series(range(5), index=['a', 'b', 'c', 'd', 'e'], dtype=dtype) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.IntSexpVector) @pytest.mark.parametrize('dtype', (pandas.Int32Dtype() if has_pandas else None, pandas.Int64Dtype() if has_pandas else None)) def test_dataframe_int_nan(self, dtype): a = pandas.DataFrame([(numpy.NaN,)], dtype=dtype, columns=['z']) with localconverter(default_converter + rpyp.converter) as _: b = robjects.conversion.py2rpy(a) assert type(b[0][0]) == rinterface.na_values.NAIntegerType with localconverter(default_converter + rpyp.converter) as _: c = robjects.conversion.rpy2py(b) @pytest.mark.parametrize('dtype', (pandas.Int32Dtype() if has_pandas else None, pandas.Int64Dtype() if has_pandas else None)) def test_series_int_nan(self, dtype): a = pandas.Series((numpy.NaN,), dtype=dtype, index=['z']) with localconverter(default_converter + rpyp.converter) as _: b = robjects.conversion.py2rpy(a) assert type(b[0]) == rinterface.na_values.NAIntegerType with localconverter(default_converter + rpyp.converter) as _: c = robjects.conversion.rpy2py(b) def test_series_obj_str(self): Series = pandas.core.series.Series s = Series(['x', 'y', 'z'], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.StrSexpVector) s = Series(['x', 'y', None], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.StrSexpVector) def test_series_obj_mixed(self): Series = pandas.core.series.Series s = Series(['x', 1, False], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: with pytest.raises(ValueError): rp_s = robjects.conversion.py2rpy(s) s = Series(['x', 1, None], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: with pytest.raises(ValueError): rp_s = robjects.conversion.py2rpy(s) def test_series_obj_bool(self): Series = pandas.core.series.Series s = Series([True, False, True], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.BoolSexpVector) s = Series([True, False, None], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.BoolSexpVector) def test_series_obj_allnone(self): Series = pandas.core.series.Series s = Series([None, None, None], index=['a', 'b', 'c']) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) assert isinstance(rp_s, rinterface.BoolSexpVector) def test_series_issue264(self): Series = pandas.core.series.Series s = Series(('a', 'b', 'c', 'd', 'e'), index=pandas.Int64Index([0,1,2,3,4])) with localconverter(default_converter + rpyp.converter) as cv: rp_s = robjects.conversion.py2rpy(s) # segfault before the fix str(rp_s) assert isinstance(rp_s, rinterface.StrSexpVector) def test_object2String(self): series = pandas.Series(["a","b","c","a"], dtype="O") with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(series) assert isinstance(rp_c, rinterface.StrSexpVector) def test_object2String_with_None(self): series = pandas.Series([None, "a","b","c","a"], dtype="O") with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(series) assert isinstance(rp_c, rinterface.StrSexpVector) def test_factor2Category(self): factor = robjects.vectors.FactorVector(('a', 'b', 'a')) with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.rpy2py(factor) assert isinstance(rp_c, pandas.Categorical) def test_factorwithNA2Category(self): factor = robjects.vectors.FactorVector(('a', 'b', 'a', None)) assert factor[3] == rinterface.na_values.NA_Integer with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.rpy2py(factor) assert isinstance(rp_c, pandas.Categorical) assert math.isnan(rp_c[3]) def test_orderedFactor2Category(self): factor = robjects.vectors.FactorVector(('a', 'b', 'a'), ordered=True) with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.rpy2py(factor) assert isinstance(rp_c, pandas.Categorical) def test_category2Factor(self): category = pandas.Series(["a","b","c","a"], dtype="category") with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(category) assert isinstance(rp_c, robjects.vectors.FactorVector) def test_categorywithNA2Factor(self): category = pandas.Series(['a', 'b', 'c', numpy.nan], dtype='category') with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(category) assert isinstance(rp_c, robjects.vectors.FactorVector) assert rp_c[3] == rinterface.NA_Integer def test_orderedCategory2Factor(self): category = pandas.Series(pandas.Categorical(['a','b','c','a'], categories=['a','b','c'], ordered=True)) with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(category) assert isinstance(rp_c, robjects.vectors.FactorVector) def test_datetime2posixct(self): datetime = pandas.Series( pandas.date_range('2017-01-01 00:00:00.234', periods=20, freq='ms', tz='UTC') ) with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(datetime) assert isinstance(rp_c, robjects.vectors.POSIXct) assert int(rp_c[0]) == 1483228800 assert int(rp_c[1]) == 1483228800 assert rp_c[0] != rp_c[1] def test_datetime2posixct_withNA(self): datetime = pandas.Series( pandas.date_range('2017-01-01 00:00:00.234', periods=20, freq='ms', tz='UTC') ) datetime[1] = pandas.NaT with localconverter(default_converter + rpyp.converter) as cv: rp_c = robjects.conversion.py2rpy(datetime) assert isinstance(rp_c, robjects.vectors.POSIXct) assert int(rp_c[0]) == 1483228800 assert math.isnan(rp_c[1]) assert rp_c[0] != rp_c[1] def test_timeR2Pandas(self): tzone = robjects.vectors.get_timezone() dt = [datetime(1960, 5, 2), datetime(1970, 6, 3), datetime(2012, 7, 1)] dt = [x.replace(tzinfo=tzone) for x in dt] # fix the time ts = [x.timestamp() for x in dt] # Create an R POSIXct vector. r_time = robjects.baseenv['as.POSIXct']( rinterface.FloatSexpVector(ts), origin=rinterface.StrSexpVector(('1970-01-01',)) ) # Convert R POSIXct vector to pandas-compatible vector with localconverter(default_converter + rpyp.converter) as cv: py_time = robjects.conversion.rpy2py(r_time) # Check that the round trip did not introduce changes for expected, obtained in zip(dt, py_time): assert expected == obtained.to_pydatetime() # Try with NA. r_time[1] = rinterface.na_values.NA_Real # Convert R POSIXct vector to pandas-compatible vector with localconverter(default_converter + rpyp.converter) as cv: py_time = robjects.conversion.rpy2py(r_time) assert py_time[1] is pandas.NaT def test_posixct_in_dataframe_to_pandas(self): tzone = robjects.vectors.get_timezone() dt = [datetime(1960, 5, 2), datetime(1970, 6, 3), datetime(2012, 7, 1)] dt = [x.replace(tzinfo=tzone) for x in dt] # fix the time ts = [x.timestamp() for x in dt] # Create an R data.frame with a posixct_vector. r_dataf = robjects.vectors.DataFrame({ 'mydate': robjects.baseenv['as.POSIXct']( rinterface.FloatSexpVector(ts), origin=rinterface.StrSexpVector(('1970-01-01',)) )}) # Convert R POSIXct vector to pandas-compatible vector with localconverter(default_converter + rpyp.converter): py_dataf = robjects.conversion.rpy2py(r_dataf) assert pandas.core.dtypes.common.is_datetime64_any_dtype(py_dataf['mydate']) def test_repr(self): # this should go to testVector, with other tests for repr() l = (('b', numpy.array([True, False, True], dtype=numpy.bool_)), ('i', numpy.array([1, 2, 3], dtype="i")), ('f', numpy.array([1, 2, 3], dtype="f")), ('s', numpy.array(["a", "b", "c"], dtype="S")), ('u', numpy.array([u"a", u"b", u"c"], dtype="U"))) od = OrderedDict(l) pd_df = pandas.core.frame.DataFrame(od) with localconverter(default_converter + rpyp.converter) as cv: rp_df = robjects.conversion.py2rpy(pd_df) s = repr(rp_df) # used to fail with a TypeError. s = s.split('\n') repr_str = ('[BoolSex..., IntSexp..., FloatSe..., ' 'ByteSex..., StrSexp...]') assert repr_str == s[1].strip() # Try again with the conversion still active. with localconverter(default_converter + rpyp.converter) as cv: rp_df = robjects.conversion.py2rpy(pd_df) s = repr(rp_df) # used to fail with a TypeError. s = s.split('\n') assert repr_str == s[1].strip() def test_ri2pandas(self): rdataf = robjects.r('data.frame(a=1:2, ' ' b=I(c("a", "b")), ' ' c=c("a", "b"))') with localconverter(default_converter + rpyp.converter) as cv: pandas_df = robjects.conversion.rpy2py(rdataf) assert isinstance(pandas_df, pandas.DataFrame) assert ('a', 'b', 'c') == tuple(pandas_df.keys()) assert pandas_df['a'].dtype in (numpy.dtype('int32'), numpy.dtype('int64')) assert pandas_df['b'].dtype == numpy.dtype('O') assert isinstance(pandas_df['c'].dtype, pandas.api.types.CategoricalDtype) def test_ri2pandas(self): rdataf = robjects.r('data.frame(a=1:2, ' ' row.names=c("a", "b"))') with localconverter(default_converter + rpyp.converter) as cv: pandas_df = cv.rpy2py(rdataf) assert all(x == y for x, y in zip(rdataf.rownames, pandas_df.index)) def test_ri2pandas_issue207(self): d = robjects.DataFrame({'x': 1}) with localconverter(default_converter + rpyp.converter) as cv: try: ok = True robjects.globalenv['d'] = d except ValueError: ok = False finally: if 'd' in robjects.globalenv: del(robjects.globalenv['d']) assert ok rpy2-3.2.6/rpy2/tests/robjects/test_serialization.py0000644000175000017500000000117213576515767023752 0ustar laurentlaurent00000000000000import pytest import pickle import tempfile from rpy2 import robjects def test_pickle(): tmp_file = tempfile.NamedTemporaryFile() robj = robjects.baseenv["pi"] pickle.dump(robj, tmp_file) tmp_file.flush() tmp_file.seek(0) robj_again = pickle.load(tmp_file) tmp_file.close() assert isinstance(robj, robjects.FloatVector) # Check that underlying R objects are identical. assert robjects.baseenv["identical"](robj, robj_again)[0] # Check the instance dict is also identical assert set(robj.__dict__.keys()) == set(robj_again.__dict__.keys()) rpy2-3.2.6/rpy2/tests/robjects/lib/0000755000175000017500000000000013615572523020214 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/robjects/lib/test_dplyr.py0000644000175000017500000000516613615570032022760 0ustar laurentlaurent00000000000000import pytest # Try to load R dplyr package, and see if it works from rpy2.rinterface_lib.embedded import RRuntimeError has_dplyr = None try: from rpy2.robjects.lib import dplyr has_dplyr = True except RRuntimeError: has_dplyr = False from rpy2.robjects.packages import importr, data datasets = importr('datasets') mtcars = data(datasets).fetch('mtcars')['mtcars'] @pytest.mark.skipif(not has_dplyr, reason='R package dplyr is not installed.') class TestDplyr(object): def test_dataframe(self): dataf = dplyr.DataFrame(mtcars) # FIXME: no testing much at the moment... assert isinstance(dataf, dplyr.DataFrame) def test_filter_nofilter_method(self): dataf = dplyr.DataFrame(mtcars) dataf_filter = dataf.filter() assert dataf.nrow == dataf_filter.nrow def test_filter_nofilter_function(self): dataf = dplyr.DataFrame(mtcars) dataf_filter = dplyr.filter(dataf) assert dataf.nrow == dataf_filter.nrow def test_filter_onefilter_method(self): dataf = dplyr.DataFrame(mtcars) ngear_gt_3 = len(tuple(x for x in dataf.rx2('gear') if x > 3)) dataf_filter = dataf.filter('gear > 3') assert ngear_gt_3 == dataf_filter.nrow def test_filter_onefilter_function(self): dataf = dplyr.DataFrame(mtcars) ngear_gt_3 = len(tuple(x for x in dataf.rx2('gear') if x > 3)) dataf_filter = dplyr.filter(dataf, 'gear > 3') assert ngear_gt_3 == dataf_filter.nrow def test_splitmerge_function(self): dataf = dplyr.DataFrame(mtcars) dataf_by_gear = dataf.group_by('gear') dataf_sum_gear = dataf_by_gear.summarize(foo='sum(gear)') assert type(dataf_sum_gear) is dplyr.DataFrame def test_join(self): dataf_a = dplyr.DataFrame(mtcars) dataf_b = dataf_a.mutate(foo=1) dataf_c = dataf_a.inner_join(dataf_b, by=dataf_a.colnames) all_names = list(dataf_a.colnames) all_names.append('foo') assert sorted(list(all_names)) == sorted(list(dataf_c.colnames)) assert tuple(all_names) == tuple(dataf_c.colnames) def test_collect(self): dataf = dplyr.DataFrame(mtcars) dataf_collected = dataf.collect() # FIXME: no real test here. Just ensuring that it is returning # without error assert type(dataf_collected) is dplyr.DataFrame def test_arrange(self): dataf = dplyr.DataFrame(mtcars) dataf_arrange = dataf.arrange('mpg') assert tuple(sorted(dataf.collect().rx2('mpg'))) == \ tuple(dataf_arrange.collect().rx2('mpg')) rpy2-3.2.6/rpy2/tests/robjects/lib/test_tidyr.py0000644000175000017500000000276613615570032022764 0ustar laurentlaurent00000000000000import pytest from rpy2.rinterface_lib.embedded import RRuntimeError # Try to load R dplyr package, and see if it works has_tidyr = None try: from rpy2.robjects.lib import tidyr has_tidyr = True except RRuntimeError: has_tidyr = False from rpy2 import rinterface from rpy2.robjects import vectors from rpy2.robjects.packages import (importr, data) datasets = importr('datasets') mtcars = data(datasets).fetch('mtcars')['mtcars'] @pytest.mark.skipif(not has_tidyr, reason='tidyr package not available in R') class TestTidyr(object): def test_dataframe(self): dataf = tidyr.DataFrame( {'x': vectors.IntVector((1,2,3,4,5)), 'labels': vectors.StrVector(('a','b','b','b','a'))}) assert isinstance(dataf, tidyr.DataFrame) assert sorted(['x', 'labels']) == sorted(list(dataf.colnames)) def test_spread(self): labels = ('a','b','c','d','e') dataf = tidyr.DataFrame( {'x': vectors.IntVector((1,2,3,4,5)), 'labels': vectors.StrVector(labels)}) dataf_spread = dataf.spread('labels', 'x') assert sorted(list(labels)) == sorted(list(dataf_spread.colnames)) def test_gather(self): dataf = tidyr.DataFrame({'a': 1.0, 'b': 2.0}) dataf_gathered = dataf.gather('label', 'x') assert sorted(['label', 'x']) == sorted(list(dataf_gathered.colnames)) rpy2-3.2.6/rpy2/tests/robjects/lib/test_ggplot2.py0000644000175000017500000000373213615570032023201 0ustar laurentlaurent00000000000000import pytest # Try to load R ggplot package, and see if it works from rpy2.rinterface_lib.embedded import RRuntimeError has_ggplot = True try: from rpy2.robjects.lib import ggplot2 except RRuntimeError: has_ggplot = False from rpy2.robjects.packages import importr datasets = importr('datasets') mtcars = datasets.__rdata__.fetch('mtcars')['mtcars'] @pytest.mark.skipif(not has_ggplot, reason='R package ggplot is not installed.') class TestGGplot(object): def test_gglot(self): gp = ggplot2.ggplot(mtcars) assert isinstance(gp, ggplot2.GGPlot) def test_element_text(self): et = ggplot2.element_text() assert isinstance(et, ggplot2.ElementText) def test_element_text_repr(self): et = ggplot2.element_text() assert repr(et).startswith(' 0 rpy2-3.2.6/rpy2/tests/robjects/test_translated_function.py0000644000175000017500000000165713576515767025153 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface import array identical = rinterface.baseenv['identical'] Function = robjects.functions.Function SignatureTranslatedFunction = robjects.functions.SignatureTranslatedFunction def test_init_invalid(): with pytest.raises(ValueError): SignatureTranslatedFunction('a') def test_init(): ri_f = rinterface.baseenv.find('rank') ro_f = SignatureTranslatedFunction(ri_f) assert identical(ri_f, ro_f)[0] is True def test_init_with_translation(): ri_f = rinterface.baseenv.find('rank') ro_f = SignatureTranslatedFunction( ri_f, init_prm_translate = {'foo_bar': 'na.last'}) assert identical(ri_f, ro_f)[0] is True def test_call(): ri_f = rinterface.baseenv.find('sum') ro_f = robjects.Function(ri_f) ro_v = robjects.IntVector(array.array('i', [1,2,3])) s = ro_f(ro_v) assert s[0] == 6 rpy2-3.2.6/rpy2/tests/robjects/test_language.py0000644000175000017500000000151013576515767022654 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects import rpy2.robjects.language as lg rinterface = robjects.rinterface @pytest.fixture(scope='module') def clean_globalenv(): yield for name in robjects.globalenv.keys(): del robjects.globalenv[name] def test_eval(clean_globalenv): code = """ x <- 1+2 y <- (x+1) / 2 """ res = lg.eval(code) assert 'x' in robjects.globalenv.keys() assert robjects.globalenv['x'][0] == 3 assert 'y' in robjects.globalenv.keys() assert robjects.globalenv['y'][0] == 2 def testeval_in_environment(clean_globalenv): code = """ x <- 1+2 y <- (x+1) / 2 """ env = robjects.Environment() res = lg.eval(code, envir=env) assert 'x' in env.keys() assert env['x'][0] == 3 assert 'y' in env.keys() assert env['y'][0] == 2 rpy2-3.2.6/rpy2/tests/robjects/test_help.py0000644000175000017500000000334313576515767022027 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects import rpy2.robjects.help as rh rinterface = robjects.rinterface class TestPackage(object): def test_init(self): base_help = rh.Package('base') assert base_help.name == 'base' def test_repr(self): base_help = rh.Package('base') assert isinstance(repr(base_help), str) class TestPage(object): def test_init(self): base_help = rh.Package('base') p = base_help.fetch('print') assert tuple(p.sections.keys())[0] == 'title' def test_fetch(self): base_help = rh.Package('base') f = base_help.fetch('print') assert 'title' in f.sections.keys() def test_to_docstring(self): base_help = rh.Package('base') p = base_help.fetch('print') ds = p.to_docstring() assert ds[:5] == 'title' def test_title(self): base_help = rh.Package('base') p = base_help.fetch('print') d = p.title() assert isinstance(d, str) assert len(d) > 0 def test_description(self): base_help = rh.Package('base') p = base_help.fetch('print') d = p.description() assert isinstance(d, str) assert len(d) > 0 def test_seealso(self): base_help = rh.Package('base') p = base_help.fetch('print') d = p.seealso() assert isinstance(d, str) assert len(d) > 0 def test_usage(self): base_help = rh.Package('base') p = base_help.fetch('print') d = p.usage() assert isinstance(d, str) assert len(d) > 0 def test_pages(): pages = rh.pages('plot') assert isinstance(pages, tuple) assert all(isinstance(elt, rh.Page) for elt in pages) rpy2-3.2.6/rpy2/tests/robjects/test_array.py0000644000175000017500000000755413576515767022225 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface import array # TODO: move to common module for testing def almost_equal(x, y, epsilon = 0.00001): return abs(y - x) <= epsilon def test_init_invalid(): with pytest.raises(TypeError): robjects.vectors.IntArray(3) def test_init(): m = rinterface.globalenv.find('matrix')(1, nrow=5, ncol=3) a = robjects.vectors.FloatArray(m) assert tuple(a.rclass) == ('matrix', ) def test_dim(): m = robjects.r.matrix(1, nrow=5, ncol=3) a = robjects.vectors.FloatArray(m) d = a.dim assert len(d) == 2 assert d[0] == 5 assert d[1] == 3 def test_names_get(): dimnames = robjects.r.list(robjects.StrVector(['a', 'b', 'c']), robjects.StrVector(['d', 'e'])) m = robjects.r.matrix(1, nrow=3, ncol=2, dimnames = dimnames) a = robjects.vectors.FloatArray(m) res = a.names r_identical = robjects.r.identical assert r_identical(dimnames[0], res[0])[0] assert r_identical(dimnames[1], res[1])[0] def test_names_set(): dimnames = robjects.r.list(robjects.StrVector(['a', 'b', 'c']), robjects.StrVector(['d', 'e'])) m = robjects.r.matrix(1, nrow=3, ncol=2) a = robjects.vectors.FloatArray(m) a.names = dimnames res = a.names r_identical = robjects.r.identical assert r_identical(dimnames[0], res[0])[0] assert r_identical(dimnames[1], res[1])[0] # Test Matrix def test_nrow_get(): m = robjects.r.matrix(robjects.IntVector(range(6)), nrow=3, ncol=2) assert m.nrow == 3 def test_ncol_get(): m = robjects.r.matrix(robjects.IntVector(range(6)), nrow=3, ncol=2) assert m.ncol == 2 def test_transpose(): m = robjects.r.matrix(robjects.IntVector(range(6)), nrow=3, ncol=2) mt = m.transpose() for i,val in enumerate((0,1,2,3,4,5,)): assert m[i] == val for i,val in enumerate((0,3,1,4,2,5)): assert mt[i] == val def test_matmul(): # 1 3 # 2 4 m = robjects.r.matrix(robjects.IntVector(range(1, 5)), nrow=2) # 1*1+3*2 1*3+3*4 # 2*1+4*2 2*3+4*4 m2 = m @ m for i,val in enumerate((7.0, 10.0, 15.0, 22.0,)): assert m2[i] == val def test_crossprod(): m = robjects.r.matrix(robjects.IntVector(range(4)), nrow=2) mcp = m.crossprod(m) for i,val in enumerate((1.0,3.0,3.0,13.0,)): assert mcp[i] == val def test_tcrossprod(): m = robjects.r.matrix(robjects.IntVector(range(4)), nrow=2) mtcp = m.tcrossprod(m) for i,val in enumerate((4,6,6,10,)): assert mtcp[i] == val def test_svd(): m = robjects.r.matrix(robjects.IntVector((1, -1, -1, 1)), nrow=2) res = m.svd() for i,val in enumerate(res.rx2("d")): assert almost_equal((2, 0)[i], val) def test_eigen(): m = robjects.r.matrix(robjects.IntVector((1, -1, -1, 1)), nrow=2) res = m.eigen() for i, val in enumerate(res.rx2("values")): assert (2, 0)[i] == val def test_dot(): m = robjects.r.matrix(robjects.IntVector(range(4)), nrow=2, ncol=2) m2 = m.dot(m) assert tuple(m2) == (2,3,6,11) def test_colnames(): m = robjects.r.matrix(robjects.IntVector(range(4)), nrow=2, ncol=2) assert m.colnames == rinterface.NULL m.colnames = robjects.StrVector(('a', 'b')) assert len(m.colnames) == 2 assert m.colnames[0] == 'a' assert m.colnames[1] == 'b' with pytest.raises(ValueError): m.colnames = robjects.StrVector(('a', 'b', 'c')) def test_rownames(): m = robjects.r.matrix(robjects.IntVector(range(4)), nrow=2, ncol=2) assert m.rownames == rinterface.NULL m.rownames = robjects.StrVector(('c', 'd')) assert len(m.rownames) == 2 assert m.rownames[0] == 'c' assert m.rownames[1] == 'd' with pytest.raises(ValueError): m.rownames = robjects.StrVector(('a', 'b', 'c')) rpy2-3.2.6/rpy2/tests/robjects/test_vector_datetime.py0000644000175000017500000000540313615434564024241 0ustar laurentlaurent00000000000000import datetime import pytest import pytz import time from rpy2 import robjects _dateval_tuple = (1984, 1, 6, 6, 22, 0, 1, 6, 0) def test_POSIXlt_from_invalidpythontime(): x = [time.struct_time(_dateval_tuple), time.struct_time(_dateval_tuple)] x.append('foo') with pytest.raises(ValueError): robjects.POSIXlt(x) def test_POSIXlt_from_pythontime(): x = [time.struct_time(_dateval_tuple), time.struct_time(_dateval_tuple)] res = robjects.POSIXlt(x) assert len(x) == 2 @pytest.mark.xfail(reason='wday mismatch between R and Python (issue #523)') def test_POSIXlt_getitem(): x = [time.struct_time(_dateval_tuple), time.struct_time(_dateval_tuple)] res = robjects.POSIXlt(x) assert res[0] == x[0] def testPOSIXlt_repr(): x = [time.struct_time(_dateval_tuple), time.struct_time(_dateval_tuple)] res = robjects.POSIXlt(x) s = repr(res) assert isinstance(s, str) def test_POSIXct_from_invalidpythontime(): x = [time.struct_time(_dateval_tuple), time.struct_time(_dateval_tuple)] x.append('foo') # string 'foo' does not have attribute 'tm_zone' with pytest.raises(AttributeError): robjects.POSIXct(x) def test_POSIXct_from_invalidobject(): x = ['abc', 3] with pytest.raises(TypeError): robjects.POSIXct(x) def test_POSIXct_from_pythontime(): x = [time.struct_time(_dateval_tuple), time.struct_time(_dateval_tuple)] res = robjects.POSIXct(x) assert len(x) == 2 def testPOSIXct_fromPythonDatetime(): x = [datetime.datetime(*_dateval_tuple[:-2]), datetime.datetime(*_dateval_tuple[:-2])] res = robjects.POSIXct(x) assert len(x) == 2 def testPOSIXct_fromSexp(): sexp = robjects.r('ISOdate(2013, 12, 11)') res = robjects.POSIXct(sexp) assert len(res) == 1 def testPOSIXct_repr(): sexp = robjects.r('ISOdate(2013, 12, 11)') res = robjects.POSIXct(sexp) s = repr(res) assert s.endswith('[2013-12-1...]') def testPOSIXct_getitem(): dt = ((datetime.datetime(2014, 12, 11) - datetime.datetime(1970,1,1)) .total_seconds()) sexp = robjects.r('ISOdate(c(2013, 2014), 12, 11, hour = 0, tz = "UTC")') res = robjects.POSIXct(sexp) assert (res[1] - dt) == 0 def testPOSIXct_iter_localized_datetime(): timestamp = 1234567890 timezone = 'UTC' r_vec = robjects.r('as.POSIXct')( timestamp, origin='1960-01-01', tz=timezone) r_times = robjects.r('strftime')(r_vec, format="%H:%M:%S", tz=timezone) py_value = next(r_vec.iter_localized_datetime()) assert r_times[0] == ':'.join( ('%i' % getattr(py_value, x) for x in ('hour', 'minute', 'second')) ) rpy2-3.2.6/rpy2/tests/robjects/test_environment.py0000644000175000017500000000455613576515767023452 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface import array def test_init_empty(): env = robjects.Environment() assert env.typeof == rinterface.RTYPES.ENVSXP def test_init_invalid(): with pytest.raises(ValueError): robjects.Environment('a') def test_getsetitem(): env = robjects.Environment() env['a'] = 123 assert 'a' in env a = env['a'] assert len(a) == 1 assert a[0] == 123 def test_keys(): env = robjects.Environment() env['a'] = 123 env['b'] = 234 keys = list(env.keys()) assert len(keys) == 2 keys.sort() for it_a, it_b in zip(keys, ('a', 'b')): assert it_a == it_b def test_values(): env = robjects.Environment() env['a'] = 123 env['b'] = 234 values = list(env.values()) assert len(values) == 2 values.sort(key=lambda x: x[0]) for it_a, it_b in zip(values, (123, 234)): assert len(it_a) == 1 assert it_a[0] == it_b def test_items(): env = robjects.Environment() env['a'] = 123 env['b'] = 234 items = list(env.items()) assert len(items) == 2 items.sort(key=lambda x: x[0]) for it_a, it_b in zip(items, (('a', 123), ('b', 234))): assert it_a[0] == it_b[0] assert it_a[1][0] == it_b[1] def test_pop_key(): env = robjects.Environment() env['a'] = 123 env['b'] = 456 robjs = [] assert len(env) == 2 robjs.append(env.pop('a')) assert len(env) == 1 robjs.append(env.pop('b')) assert len(env) == 0 assert [x[0] for x in robjs] == [123, 456] with pytest.raises(KeyError): env.pop('c') assert env.pop('c', 789) == 789 with pytest.raises(ValueError): env.pop('c', 1, 2) def test_popitem(): env = robjects.Environment() env['a'] = 123 env['b'] = 456 robjs = [] assert len(env) == 2 robjs.append(env.popitem()) assert len(env) == 1 robjs.append(env.popitem()) assert len(env) == 0 assert sorted([(k, v[0]) for k, v in robjs]) == [('a', 123), ('b', 456)] with pytest.raises(KeyError): robjs.append(env.popitem()) def test_clear(): env = robjects.Environment() env['a'] = 123 env['b'] = 234 assert len(env) == 2 env.clear() assert len(env) == 0 rpy2-3.2.6/rpy2/tests/robjects/test_function.py0000644000175000017500000000264713576515767022732 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface import array identical = rinterface.baseenv['identical'] Function = robjects.functions.Function def test_init_invalid(): with pytest.raises(ValueError): Function('a') def test_init_from_existing(): ri_f = rinterface.baseenv.find('sum') ro_f = Function(ri_f) assert identical(ri_f, ro_f)[0] == True def test_call_with_sexp(): ri_f = rinterface.baseenv.find('sum') ro_f = Function(ri_f) ro_v = robjects.IntVector(array.array('i', [1,2,3])) s = ro_f(ro_v) assert s[0] == 6 def test_formals(): ri_f = robjects.r('function(x, y) TRUE') res = ri_f.formals() #FIXME: no need for as.list when paired list are handled res = robjects.r['as.list'](res) assert len(res) == 2 n = res.names assert n[0] == 'x' assert n[1] == 'y' def test_function(): r_func = robjects.functions.Function(robjects.r('function(x, y) TRUE')) assert isinstance(r_func.__doc__, str) def test_signaturestranslatedfunction(): r_func = robjects.r('function(x, y) TRUE') stf = robjects.functions.SignatureTranslatedFunction(r_func) assert isinstance(r_func.__doc__, str) def test_documentedstfunction(): dstf = robjects.functions.DocumentedSTFunction(robjects.baseenv['sum'], packagename='base') assert isinstance(dstf.__doc__, str) rpy2-3.2.6/rpy2/tests/robjects/test_dataframe.py0000644000175000017500000001127413576515767023025 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface import rpy2.rlike.container as rlc import array import csv import os import tempfile def test_init_from_taggedlist(): letters = robjects.r.letters numbers = robjects.r('1:26') df = robjects.DataFrame(rlc.TaggedList((letters, numbers), tags = ('letters', 'numbers'))) assert df.rclass[0] == 'data.frame' def test_init_from_RObject(): numbers = robjects.r('1:5') dataf = robjects.DataFrame(numbers) assert len(dataf) == 5 assert all(len(x) == 1 for x in dataf) rfunc = robjects.r('sum') with pytest.raises(ValueError): robjects.DataFrame(rfunc) rdataf = robjects.r('data.frame(a=1:2, b=c("a", "b"))') dataf = robjects.DataFrame(rdataf) # TODO: test ? def test_init_from_OrdDict(): od = rlc.OrdDict(c=(('a', robjects.IntVector((1,2))), ('b', robjects.StrVector(('c', 'd'))) )) dataf = robjects.DataFrame(od) assert dataf.rx2('a')[0] == 1 def test_init_from_dict(): od = {'a': robjects.IntVector((1,2)), 'b': robjects.StrVector(('c', 'd'))} dataf = robjects.DataFrame(od) assert dataf.rx2('a')[0] == 1 def test_init_stringsasfactors(): od = {'a': robjects.IntVector((1,2)), 'b': robjects.StrVector(('c', 'd'))} dataf = robjects.DataFrame(od, stringsasfactor=True) assert isinstance(dataf.rx2('b'), robjects.FactorVector) dataf = robjects.DataFrame(od, stringsasfactor=False) assert isinstance(dataf.rx2('b'), robjects.StrVector) def test_dim(): letters = robjects.r.letters numbers = robjects.r('1:26') df = robjects.DataFrame(rlc.TaggedList((letters, numbers), tags = ('letters', 'numbers'))) assert df.nrow == 26 assert df.ncol == 2 def test_from_csvfile(): column_names = ('letter', 'value') data = (column_names, ('a', 1), ('b', 2), ('c', 3)) fh = tempfile.NamedTemporaryFile(mode = "w", delete = False) csv_w = csv.writer(fh) csv_w.writerows(data) fh.close() dataf = robjects.DataFrame.from_csvfile(fh.name) assert isinstance(dataf, robjects.DataFrame) assert column_names == tuple(dataf.names) assert dataf.nrow == 3 assert dataf.ncol == 2 def test_to_csvfile(): fh = tempfile.NamedTemporaryFile(mode = "w", delete = False) fh.close() d = {'letter': robjects.StrVector('abc'), 'value' : robjects.IntVector((1, 2, 3))} dataf = robjects.DataFrame(d) dataf.to_csvfile(fh.name) dataf = robjects.DataFrame.from_csvfile(fh.name) assert dataf.nrow == 3 assert dataf.ncol == 2 def test_iter_col(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') col_types = [x.typeof for x in dataf.iter_column()] assert rinterface.RTYPES.INTSXP == col_types[0] assert rinterface.RTYPES.STRSXP == col_types[1] def test_iter_row(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') rows = [x for x in dataf.iter_row()] assert rows[0][0][0] == 1 assert rows[1][1][0] == 'b' def test_colnames(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') assert dataf.rownames[0] == '1' assert dataf.rownames[1] == '2' def test_colnames_set(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') dataf.colnames = robjects.StrVector('de') assert tuple(dataf.colnames) == ('d', 'e') def test_rownames(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') assert tuple(dataf.colnames) == ('a', 'b') def test_rownames_set(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') dataf.rownames = robjects.StrVector('de') assert tuple(dataf.rownames) == ('d', 'e') def test_cbind(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') dataf = dataf.cbind(robjects.r('data.frame(a=1:2, b=I(c("a", "b")))')) assert dataf.ncol == 4 assert len([x for x in dataf.colnames if x == 'a']) == 2 dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') dataf = dataf.cbind(a = robjects.StrVector(("c", "d"))) assert dataf.ncol == 3 assert len([x for x in dataf.colnames if x == 'a']) == 2 def test_rbind(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') dataf = dataf.rbind(dataf) assert dataf.ncol == 2 assert dataf.nrow == 4 def test_head(): dataf = robjects.r('data.frame(a=1:26, b=I(letters))') assert dataf.head(5).nrow == 5 assert dataf.head(5).ncol == 2 def test_repr(): dataf = robjects.r('data.frame(a=1:2, b=I(c("a", "b")))') s = repr(dataf) assert 'data.frame' in s.split(os.linesep)[0] rpy2-3.2.6/rpy2/tests/robjects/__init__.py0000644000175000017500000000000013576515767021562 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/robjects/test_rs4.py0000644000175000017500000000123513576515767021605 0ustar laurentlaurent00000000000000import pytest from rpy2 import robjects @pytest.fixture(scope='module') def set_class_A(): robjects.r('setClass("A", representation(a="numeric", b="character"))') yield robjects.r('setClass("A")') def test_slotnames(set_class_A): ainstance = robjects.r('new("A", a=1, b="c")') assert tuple(ainstance.slotnames()) == ('a', 'b') def test_isclass(set_class_A): ainstance = robjects.r('new("A", a=1, b="c")') assert not ainstance.isclass("B") assert ainstance.isclass("A") def test_validobject(set_class_A): ainstance = robjects.r('new("A", a=1, b="c")') assert ainstance.validobject() #FIXME: test invalid objects ? rpy2-3.2.6/rpy2/tests/robjects/test_vector_factor.py0000644000175000017500000000240513576515767023735 0ustar laurentlaurent00000000000000import pytest from rpy2 import robjects def test_init(): vec = robjects.FactorVector(robjects.StrVector('abaabc')) assert len(vec) == 6 def test_factor_repr(): vec = robjects.vectors.FactorVector(('abc', 'def', 'ghi')) s = repr(vec) assert s.endswith('[abc, def, ghi]') def test_isordered(): vec = robjects.FactorVector(robjects.StrVector('abaabc')) assert vec.isordered is False def test_nlevels(): vec = robjects.FactorVector(robjects.StrVector('abaabc')) assert vec.nlevels == 3 def test_levels(): vec = robjects.FactorVector(robjects.StrVector('abaabc')) assert len(vec.levels) == 3 assert set(('a','b','c')) == set(tuple(vec.levels)) def test_levels_set(): vec = robjects.FactorVector(robjects.StrVector('abaabc')) vec.levels = robjects.vectors.StrVector('def') assert set(('d','e','f')) == set(tuple(vec.levels)) def test_iter_labels(): values = 'abaabc' vec = robjects.FactorVector(robjects.StrVector(values)) it = vec.iter_labels() for a, b in zip(values, it): assert a == b def test_factor_with_attrs(): # issue #299 r_src = """ x <- factor(c("a","b","a")) attr(x, "foo") <- "bar" x """ x = robjects.r(r_src) assert 'foo' in x.list_attrs() rpy2-3.2.6/rpy2/tests/robjects/test_vector_extractdelegator.py0000644000175000017500000000663513576515767026031 0ustar laurentlaurent00000000000000import array import pytest import rpy2.rinterface_lib.callbacks import rpy2.rinterface_lib.embedded import rpy2.rlike.container as rlc from rpy2 import robjects from .. import utils def _just_pass(s): pass @pytest.fixture(scope='module') def silent_console_print(): with utils.obj_in_module(rpy2.rinterface_lib.callbacks, 'consolewrite_print', _just_pass): yield def test_extract_getitem_by_index(): seq_R = robjects.baseenv["seq"] mySeq = seq_R(0, 10) # R indexing starts at one myIndex = robjects.vectors.IntVector(array.array('i', range(1, 11, 2))) mySubset = mySeq.rx[myIndex] for i, si in enumerate(myIndex): assert mySeq[si-1] == mySubset[i] def test_extract_by_index(): seq_R = robjects.baseenv["seq"] mySeq = seq_R(0, 10) # R indexing starts at one myIndex = robjects.vectors.IntVector(array.array('i', range(1, 11, 2))) mySubset = mySeq.rx(myIndex) for i, si in enumerate(myIndex): assert mySeq[si-1] == mySubset[i] def test_extract_by_name(): seq_R = robjects.baseenv["seq"] mySeq = seq_R(0, 25) letters = robjects.baseenv["letters"] mySeq = robjects.baseenv["names<-"](mySeq, letters) # R indexing starts at one myIndex = robjects.vectors.StrVector(letters[2]) mySubset = mySeq.rx(myIndex) for i, si in enumerate(myIndex): assert mySubset[i] == 2 def test_extract_silent_index_error(): seq_R = robjects.baseenv["seq"] mySeq = seq_R(0, 10) # R indexing starts at one. myIndex = robjects.vectors.StrVector(['a', 'b', 'c']) with utils.obj_in_module(rpy2.rinterface_lib.callbacks, 'consolewrite_print', utils.noconsole): res = mySeq.rx(myIndex) assert all(x == rpy2.robjects.NA_Integer for x in res) def test_replace(): vec = robjects.vectors.IntVector(range(1, 6)) i = array.array('i', [1, 3]) vec.rx[rlc.TaggedList((i, ))] = 20 assert vec[0] == 20 assert vec[1] == 2 assert vec[2] == 20 assert vec[3] == 4 vec = robjects.vectors.IntVector(range(1, 6)) i = array.array('i', [1, 5]) vec.rx[rlc.TaggedList((i, ))] = 50 assert vec[0] == 50 assert vec[1] == 2 assert vec[2] == 3 assert vec[3] == 4 assert vec[4] == 50 vec = robjects.vectors.IntVector(range(1, 6)) vec.rx[1] = 70 assert tuple(vec[0:5]) == (70, 2, 3, 4, 5) vec = robjects.vectors.IntVector(range(1, 6)) vec.rx[robjects.vectors.IntVector((1, 3))] = 70 assert tuple(vec[0:5]) == (70, 2, 70, 4, 5) m = robjects.r('matrix(1:10, ncol=2)') m.rx[1, 1] = 9 assert m[0] == 9 m = robjects.r('matrix(1:10, ncol=2)') m.rx[2, robjects.vectors.IntVector((1,2))] = 9 assert m[1] == 9 assert m[6] == 9 def test_extract_recycling_rule(): # Create a vector with 22 cells. v = robjects.vectors.IntVector(array.array('i', range(1, 23))) # Promote it to a matrix with 2 columns (therefore 11 rows). m = robjects.r.matrix(v, ncol = 2) # Extraxt all elements in the first column (R is one-indexed). col = m.rx(True, 1) assert len(col) == 11 def test_extract_list(): # list letters = robjects.baseenv['letters'] myList = robjects.baseenv['list'](l=letters, f='foo') idem = robjects.baseenv['identical'] assert idem(letters, myList.rx('l')[0])[0] assert idem('foo', myList.rx('f')[0])[0] rpy2-3.2.6/rpy2/tests/robjects/test_packages.py0000644000175000017500000001517013615570032022632 0ustar laurentlaurent00000000000000import io import os import pytest import sys import rpy2.robjects as robjects import rpy2.robjects.help import rpy2.robjects.packages as packages import rpy2.robjects.packages_utils from rpy2.rinterface_lib.embedded import RRuntimeError rinterface = robjects.rinterface class TestPackage(object): def tests_package_from_env(self): env = robjects.Environment() env['a'] = robjects.StrVector('abcd') env['b'] = robjects.IntVector((1,2,3)) env['c'] = robjects.r(''' function(x) x^2''') pck = robjects.packages.Package(env, "dummy_package") assert isinstance(pck.a, robjects.Vector) assert isinstance(pck.b, robjects.Vector) assert isinstance(pck.c, robjects.Function) def test_new_with_dot(self): env = robjects.Environment() env['a.a'] = robjects.StrVector('abcd') env['b'] = robjects.IntVector((1,2,3)) env['c'] = robjects.r(''' function(x) x^2''') pck = robjects.packages.Package(env, "dummy_package") assert isinstance(pck.a_a, robjects.Vector) assert isinstance(pck.b, robjects.Vector) assert isinstance(pck.c, robjects.Function) def test_new_with_dot_conflict(self): env = robjects.Environment() env['a.a_a'] = robjects.StrVector('abcd') env['a_a.a'] = robjects.IntVector((1,2,3)) env['c'] = robjects.r(''' function(x) x^2''') with pytest.raises(packages.LibraryError): robjects.packages.Package(env, "dummy_package") def test_new_with_dot_conflict2(self): env = robjects.Environment() name_in_use = dir(packages.Package(env, "foo"))[0] env[name_in_use] = robjects.StrVector('abcd') with pytest.raises(packages.LibraryError): robjects.packages.Package(env, "dummy_package") def tests_package_repr(self): env = robjects.Environment() pck = robjects.packages.Package(env, "dummy_package") assert isinstance(repr(pck), str) def test_signaturetranslatedanonymouspackage(): rcode = """ square <- function(x) { return(x^2) } cube <- function(x) { return(x^3) } """ powerpack = packages.STAP(rcode, "powerpack") assert hasattr(powerpack, 'square') assert hasattr(powerpack, 'cube') def test_installedstpackage_docstring(): stats = robjects.packages.importr('stats', on_conflict='warn') assert stats.__doc__.startswith('Python representation of an R package.') def test_installedstpackage_docstring_no_rname(): stats = robjects.packages.importr('stats', on_conflict='warn') stats.__rname__ = None assert stats.__doc__.startswith('Python representation of an R package.\n' '') class TestImportr(object): def test_importr_stats(self): stats = robjects.packages.importr('stats', on_conflict='warn') assert isinstance(stats, robjects.packages.Package) def test_import_stats_with_libloc(self): path = robjects.packages_utils.get_packagepath('stats') stats = robjects.packages.importr('stats', on_conflict='warn', lib_loc = path) assert isinstance(stats, robjects.packages.Package) def test_import_stats_with_libloc_and_suppressmessages(self): path = robjects.packages_utils.get_packagepath('stats') stats = robjects.packages.importr('stats', lib_loc=path, on_conflict='warn', suppress_messages=False) assert isinstance(stats, robjects.packages.Package) def test_import_stats_with_libloc_with_quote(self): path = 'coin"coin' with pytest.raises(RRuntimeError), \ pytest.warns(UserWarning): Tmp_File = io.StringIO tmp_file = Tmp_File() try: stdout = sys.stdout sys.stdout = tmp_file robjects.packages.importr('dummy_inexistant', lib_loc=path) finally: sys.stdout = stdout tmp_file.close() def test_import_datasets(self): datasets = robjects.packages.importr('datasets') assert isinstance(datasets, robjects.packages.Package) assert isinstance(datasets.__rdata__, robjects.packages.PackageData) assert isinstance(robjects.packages.data(datasets), robjects.packages.PackageData) def test_datatsets_names(self): datasets = robjects.packages.importr('datasets') datasets_data = robjects.packages.data(datasets) datasets_names = tuple(datasets_data.names()) assert len(datasets_names) > 0 assert all(isinstance(x, str) for x in datasets_names) def test_datatsets_fetch(self): datasets = robjects.packages.importr('datasets') datasets_data = robjects.packages.data(datasets) datasets_names = tuple(datasets_data.names()) assert isinstance(datasets_data.fetch(datasets_names[0]), robjects.Environment) with pytest.raises(KeyError): datasets_data.fetch('foo_%s' % datasets_names[0]) def test_wherefrom(): stats = robjects.packages.importr('stats', on_conflict='warn') rnorm_pack = robjects.packages.wherefrom('rnorm') assert rnorm_pack.do_slot('name')[0] == 'package:stats' def test_installedpackages(): instapacks = robjects.packages.InstalledPackages() res = instapacks.isinstalled('foo') assert res is False ncols = len(instapacks.colnames) for row in instapacks: assert ncols == len(row) # TODO: Not really a unit test. The design of the code being # tested (or not tested) probably need to be re-thought. def test_parsedcode(): rcode = '1+2' expression = rinterface.parse(rcode) pc = robjects.packages.ParsedCode(expression) assert isinstance(pc, type(expression)) def test_sourcecode(): rcode = '1+2' expression = rinterface.parse(rcode) sc = robjects.packages.SourceCode(rcode) assert isinstance(sc.parse(), type(expression)) def test_sourcecode_as_namespace(): rcode = os.linesep.join( ('x <- 1+2', 'f <- function(x) x+1') ) sc = robjects.packages.SourceCode(rcode) foo_ns = sc.as_namespace('foo_ns') assert hasattr(foo_ns, 'x') assert hasattr(foo_ns, 'f') assert foo_ns.x[0] == 3 assert isinstance(foo_ns.f, rinterface.SexpClosure) rpy2-3.2.6/rpy2/tests/robjects/test_robjects.py0000644000175000017500000001037513576515767022715 0ustar laurentlaurent00000000000000import array import pytest import rpy2.robjects as robjects rinterface = robjects.rinterface # TODO: what is this ? # def tearDow(self): # robjects.r._dotter = False def test_getitem(): letters_R = robjects.r['letters'] assert isinstance(letters_R, robjects.Vector) letters = (('a', 0), ('b', 1), ('c', 2), ('x', 23), ('y', 24), ('z', 25)) for l, i in letters: assert letters_R[i] == l as_list_R = robjects.r['as.list'] seq_R = robjects.r['seq'] mySeq = seq_R(0, 10) myList = as_list_R(mySeq) for i, li in enumerate(myList): assert myList[i][0] == i def test_eval(): # vector long enough to span across more than one line x = robjects.baseenv['seq'](1, 50, 2) res = robjects.r('sum(%s)' %x.r_repr()) assert res[0] == 625 def test_override_rpy2py(): class Density(object): def __init__(self, x): self._x = x def f(obj): pyobj = robjects.default_converter.rpy2py(obj) inherits = rinterface.baseenv['inherits'] classname = rinterface.StrSexpVector(['density', ]) if inherits(pyobj, classname)[0]: pyobj = Density(pyobj) return pyobj robjects.conversion.rpy2py = f x = robjects.r.rnorm(100) d = robjects.r.density(x) assert isinstance(d, Density) def test_items(): v = robjects.IntVector((1,2,3)) rs = robjects.robject.RSlots(v) assert len(tuple(rs.items())) == 0 v.do_slot_assign('a', robjects.IntVector((9,))) for ((k_o,v_o), (k,v)) in zip((('a', robjects.IntVector), ), rs.items()): assert k_o == k assert v_o == type(v) def test_values(): v = robjects.IntVector((1,2,3)) rs = robjects.robject.RSlots(v) assert len(tuple(rs.items())) == 0 v.do_slot_assign('a', robjects.IntVector((9,))) for (v_o, v) in zip((robjects.IntVector, ), rs.values()): assert v_o == type(v) def test_init(): identical = rinterface.baseenv['identical'] py_a = array.array('i', [1,2,3]) with pytest.raises(ValueError): robjects.RObject(py_a) ri_v = rinterface.IntSexpVector(py_a) ro_v = robjects.RObject(ri_v) assert identical(ro_v, ri_v)[0] is True del(ri_v) assert rinterface.RTYPES.INTSXP == ro_v.typeof def test_r_repr(): obj = robjects.baseenv['pi'] s = obj.r_repr() assert s.startswith('3.14') def test_str(): prt = robjects.baseenv['pi'] s = prt.__str__() assert s.startswith('[1] 3.14') def test_rclass(): assert robjects.baseenv['letters'].rclass[0] == 'character' assert robjects.baseenv['pi'].rclass[0] == 'numeric' assert robjects.globalenv.find('help').rclass[0] == 'function' def test_rclass_set(): x = robjects.r('1:3') old_class = x.rclass x.rclass = robjects.StrVector(('Foo', )) + x.rclass assert x.rclass[0] == 'Foo' assert old_class[0] == x.rclass[1] def test_rclass_set_usingstring(): x = robjects.r('1:3') old_class = x.rclass x.rclass = 'Foo' assert x.rclass[0] == 'Foo' def test_rclass_str(): s = str(robjects.r) assert isinstance(s, str) def test_do_slot(): assert robjects.globalenv.find('BOD').do_slot('reference')[0] == 'A1.4, p. 270' def test_slots(): x = robjects.r('list(a=1,b=2,c=3)') s = x.slots assert len(s) == 1 assert tuple(s.keys()) == ('names', ) assert tuple(s['names']) == ('a', 'b', 'c') s['names'] = 0 assert len(s) == 1 assert tuple(s.keys()) == ('names', ) assert tuple(s['names']) == (0, ) @pytest.mark.parametrize( 'cls, values, expected_cls', [(rinterface.IntSexpVector, (1, 2), robjects.vectors.IntVector), (rinterface.FloatSexpVector, (1.1, 2.2), robjects.vectors.FloatVector), (rinterface.BoolSexpVector, (True, False), robjects.vectors.BoolVector), (rinterface.ByteSexpVector, b'ab', robjects.vectors.ByteVector), (lambda x: rinterface.evalr(x), 'y ~ x', robjects.Formula)]) def test_sexpvector_to_ro(cls, values, expected_cls): v_ri = cls(values) v_ro = robjects.sexpvector_to_ro(v_ri) assert isinstance(v_ro, expected_cls) def test_sexpvector_to_ro_error_notvector(): with pytest.raises(ValueError): robjects.sexpvector_to_ro(rinterface.globalenv) rpy2-3.2.6/rpy2/tests/robjects/test_methods.py0000644000175000017500000000566213576515767022550 0ustar laurentlaurent00000000000000import pytest import sys import textwrap import rpy2.robjects as robjects import rpy2.robjects.methods as methods rinterface = robjects.rinterface def test_set_accessors(): robjects.r['setClass']("A", robjects.r('list(foo="numeric")')) robjects.r['setMethod']("length", signature="A", definition = robjects.r("function(x) 123")) class A(methods.RS4): def __init__(self): obj = robjects.r['new']('A') super().__init__(obj) acs = (('length', None, True, None), ) methods.set_accessors(A, "A", None, acs) a = A() assert a.length[0] == 123 def test_RS4Type_noaccessors(): robjects.r['setClass']("Foo", robjects.r('list(foo="numeric")')) classdef = """ from rpy2 import robjects from rpy2.robjects import methods class Foo(methods.RS4, metaclass=methods.RS4_Type): def __init__(self): obj = robjects.r['new']('Foo') super().__init__(obj) """ code = compile(textwrap.dedent(classdef), '', 'exec') ns = dict() exec(code, ns) f = ns['Foo']() # TODO: test ? def test_RS4_factory(): rclassname = 'Foo' robjects.r['setClass'](rclassname, robjects.r('list(bar="numeric")')) obj = robjects.r['new'](rclassname) f_rs4i = methods.rs4instance_factory(obj) assert rclassname == type(f_rs4i).__name__ def test_RS4Type_accessors(): robjects.r['setClass']("R_A", robjects.r('list(foo="numeric")')) robjects.r['setMethod']("length", signature="R_A", definition = robjects.r("function(x) 123")) classdef = """ from rpy2 import robjects from rpy2.robjects import methods class R_A(methods.RS4, metaclass=methods.RS4_Type): __accessors__ = ( ('length', None, 'get_length', False, 'get the length'), ('length', None, None, True, 'length')) def __init__(self): obj = robjects.r['new']('R_A') super().__init__(obj) """ code = compile(textwrap.dedent(classdef), '', 'exec') ns = dict() exec(code, ns) R_A = ns['R_A'] class A(R_A): __rname__ = 'R_A' ra = R_A() assert ra.get_length()[0] == 123 assert ra.length[0] == 123 a = A() assert a.get_length()[0] == 123 assert a.length[0] == 123 def test_getclassdef(): robjects.r('library(stats4)') cr = methods.getclassdef('mle', 'stats4') assert not cr.virtual def test_RS4Auto_Type(): robjects.r('library(stats4)') class MLE(object, metaclass=robjects.methods.RS4Auto_Type): __rname__ = 'mle' __rpackagename__ = 'stats4' # TODO: test ? def test_RS4Auto_Type_nopackname(): robjects.r('library(stats4)') class MLE(object, metaclass=robjects.methods.RS4Auto_Type): __rname__ = 'mle' # TODO: test ? rpy2-3.2.6/rpy2/tests/robjects/test_vector.py0000644000175000017500000003011413576515767022375 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects as robjects ri = robjects.rinterface import array import time import datetime import rpy2.rlike.container as rlc from collections import OrderedDict rlist = robjects.baseenv["list"] def test_init(): identical = ri.baseenv["identical"] py_a = array.array('i', [1,2,3]) ro_v = robjects.IntVector(py_a) assert ro_v.typeof == ri.RTYPES.INTSXP ri_v = ri.IntSexpVector(py_a) ro_v = robjects.IntVector(ri_v) assert identical(ro_v, ri_v)[0] del(ri_v) assert ro_v.typeof == ri.RTYPES.INTSXP @pytest.mark.parametrize( 'cls,expected_na', [(robjects.StrVector, ri.NA_Character), (robjects.IntVector, ri.NA_Integer), (robjects.FloatVector, ri.NA_Real), (robjects.BoolVector, ri.NA_Logical), (robjects.ComplexVector, ri.NA_Complex), ]) def test_vector_navalue(cls, expected_na): assert cls.NAvalue is expected_na @pytest.mark.parametrize( 'cls,values', [(robjects.StrVector, ['abc', 'def']), (robjects.IntVector, [123, 456]), (robjects.FloatVector, [123.0, 456.0]), (robjects.BoolVector, [True, False])]) def test_init_vectors(cls, values): vec = cls(values) assert len(vec) == len(values) for x, y in zip(vec, values): assert x == y @pytest.mark.parametrize( 'vec', (robjects.ListVector({'a': 1, 'b': 2}), robjects.ListVector((('a', 1), ('b', 2))), robjects.ListVector(iter([('a', 1), ('b', 2)]))) ) def test_new_listvector(vec): assert 'a' in vec.names assert 'b' in vec.names assert len(vec) == 2 assert len(vec.names) == 2 def test_strvector_factor(): vec = robjects.StrVector(('abc', 'def', 'abc')) fvec = vec.factor() assert isinstance(fvec, robjects.FactorVector) def test_add_operator(): seq_R = robjects.r["seq"] mySeqA = seq_R(0, 3) mySeqB = seq_R(5, 7) mySeqAdd = mySeqA + mySeqB assert len(mySeqA)+len(mySeqB) == len(mySeqAdd) for i, li in enumerate(mySeqA): assert mySeqAdd[i] == li for j, li in enumerate(mySeqB): assert mySeqAdd[i+j+1] == li def test_r_add_operator(): seq_R = robjects.r["seq"] mySeq = seq_R(0, 10) mySeqAdd = mySeq.ro + 2 for i, li in enumerate(mySeq): assert li + 2 == mySeqAdd[i] def test_r_sub_operator(): seq_R = robjects.r["seq"] mySeq = seq_R(0, 10) mySeqAdd = mySeq.ro - 2 for i, li in enumerate(mySeq): assert li - 2 == mySeqAdd[i] def test_r_mult_operator(): seq_R = robjects.r["seq"] mySeq = seq_R(0, 10) mySeqAdd = mySeq.ro * 2 for i, li in enumerate(mySeq): assert li * 2 == mySeqAdd[i] def test_r_matmul_operator(): # 1 3 # 2 4 m = robjects.r.matrix(robjects.IntVector(range(1, 5)), nrow=2) # 1*1+3*2 1*3+3*4 # 2*1+4*2 2*3+4*4 m2 = m.ro @ m for i,val in enumerate((7.0, 10.0, 15.0, 22.0,)): assert m2[i] == val def test_r_power_operator(): seq_R = robjects.r["seq"] mySeq = seq_R(0, 10) mySeqPow = mySeq.ro ** 2 for i, li in enumerate(mySeq): assert li ** 2 == mySeqPow[i] def test_r_truediv(): v = robjects.vectors.IntVector((2,3,4)) res = v.ro / 2 assert all(abs(x-y) < 0.001 for x, y in zip(res, (1, 1.5, 2))) def test_r_floor_division(): v = robjects.vectors.IntVector((2, 3, 4)) res = v.ro // 2 assert tuple(int(x) for x in res) == (1, 1, 2) def test_r_mod(): v = robjects.vectors.IntVector((2, 3, 4)) res = v.ro % 2 assert all(x == y for x, y in zip(res, (0, 1, 0))) def test_r_and(): v = robjects.vectors.BoolVector((True, False)) res = v.ro & True assert all(x is y for x, y in zip(res, (True, False))) def test_r_or(): v = robjects.vectors.BoolVector((True, False)) res = v.ro | False assert all(x is y for x, y in zip(res, (True, False))) def test_r_invert(): v = robjects.vectors.BoolVector((True, False)) res = ~v.ro assert all(x is (not y) for x, y in zip(res, (True, False))) def test_r_lt(): v = robjects.vectors.IntVector((4, 2, 1)) res = v.ro < 2 assert all(x is y for x, y in zip(res, (False, False, True))) def test_r_le(): v = robjects.vectors.IntVector((4, 2, 1)) res = (v.ro <= 2) assert all(x is y for x, y in zip(res, (False, True, True))) def test_r_gt(): v = robjects.vectors.IntVector((4, 2, 1)) res = v.ro > 2 assert all(x is y for x, y in zip(res, (True, False, False))) def test_r_ge(): v = robjects.vectors.IntVector((4, 2, 1)) res = v.ro >= 2 assert all(x is y for x, y in zip(res, (True, True, False))) def test_r_eq(): v = robjects.vectors.IntVector((4, 2, 1)) res = v.ro == 2 assert all(x is y for x, y in zip(res, (False, True, False))) def test_r_ne(): v = robjects.vectors.IntVector((4, 2, 1)) res = v.ro != 2 assert all(x is y for x, y in zip(res, (True, False, True))) def test_r_neg(): v = robjects.vectors.IntVector((4, 2, 1)) res = - v.ro assert all(x is y for x, y in zip(res, (-4, -2, -1))) def test_contains(): v = robjects.StrVector(('abc', 'def', 'ghi')) assert 'def' in v.ro assert 'foo' not in v.ro @pytest.mark.parametrize( 'cls,values', [(robjects.StrVector, ['abc', 'def']), (robjects.IntVector, [123, 456]), (robjects.FloatVector, [123.0, 456.0]), (robjects.BoolVector, [True, False])]) def test_getitem_int(cls, values): vec = cls(values) assert vec[0] == values[0] assert vec[1] == values[1] def test_getitem_outofbounds(): letters = robjects.baseenv["letters"] with pytest.raises(IndexError): letters[26] @pytest.mark.parametrize( 'cls,values', [(robjects.StrVector, ['abc', 'def']), (robjects.IntVector, [123, 456]), (robjects.FloatVector, [123.0, 456.0]), (robjects.BoolVector, [True, False])]) def test_getitem_invalidtype(cls, values): vec = cls(values) with pytest.raises(TypeError): vec['foo'] def test_setitem(): vec = robjects.r.seq(1, 10) assert vec[0] == 1 vec[0] = 20 assert vec[0] == 20 def test_setitem_outofbounds(): vec = robjects.r.seq(1, 10) with pytest.raises(IndexError): vec[20] = 20 @pytest.mark.parametrize( 'cls,values', [(robjects.StrVector, ['abc', 'def']), (robjects.IntVector, [123, 456]), (robjects.FloatVector, [123.0, 456.0]), (robjects.BoolVector, [True, False])]) def test_setitem_invalidtype(cls, values): vec = cls(values) with pytest.raises(TypeError): vec['foo'] = values[0] def get_item_list(): mylist = rlist(letters, "foo") idem = robjects.baseenv["identical"] assert idem(letters, mylist[0])[0] is True assert idem("foo", mylist[1])[0] is True def test_getnames(): vec = robjects.vectors.IntVector(array.array('i', [1,2,3])) v_names = [robjects.baseenv["letters"][x] for x in (0,1,2)] #FIXME: simplify this r_names = robjects.baseenv["c"](*v_names) vec = robjects.baseenv["names<-"](vec, r_names) for i in range(len(vec)): assert v_names[i] == vec.names[i] vec.names[0] = 'x' def test_setnames(): vec = robjects.vectors.IntVector(array.array('i', [1,2,3])) names = ['x', 'y', 'z'] vec.names = names for i in range(len(vec)): assert names[i] == vec.names[i] def test_nainteger(): vec = robjects.IntVector(range(3)) vec[0] = robjects.NA_Integer assert robjects.baseenv['is.na'](vec)[0] is True def test_tabulate(): vec = robjects.IntVector((1,2,1,2,1,2,2)) tb = vec.tabulate() assert tuple(tb) == (3, 4) def test_nareal(): vec = robjects.FloatVector((1.0, 2.0, 3.0)) vec[0] = robjects.NA_Real assert robjects.baseenv['is.na'](vec)[0] is True def test_nalogical(): vec = robjects.BoolVector((True, False, True)) vec[0] = robjects.NA_Logical assert robjects.baseenv['is.na'](vec)[0] is True @pytest.mark.xfail(reason='Edge case with conversion.') def test_nacomplex(): vec = robjects.ComplexVector((1+1j, 2+2j, 3+3j)) vec[0] = robjects.NA_Complex assert robjects.baseenv['is.na'](vec)[0] is True def test_nacomplex_workaround(): vec = robjects.ComplexVector((1+1j, 2+2j, 3+3j)) vec[0] = complex(robjects.NA_Complex.real, robjects.NA_Complex.imag) assert robjects.baseenv['is.na'](vec)[0] is True def test_nacharacter(): vec = robjects.StrVector('abc') vec[0] = robjects.NA_Character assert robjects.baseenv['is.na'](vec)[0] is True def test_int_repr(): vec = robjects.vectors.IntVector((1, 2, ri.NA_Integer)) s = repr(vec) assert s.endswith('[1, 2, NA_integer_]') def test_list_repr(): vec = robjects.vectors.ListVector((('a', 1), ('b', 2), ('b', 3))) s = repr(vec) assert s.startswith("R object with classes: ('list',) ") vec2 = robjects.vectors.ListVector((('A', vec),)) s = repr(vec2) assert s.startswith("R object with classes: ('list',) ") def test_float_repr(): vec = robjects.vectors.FloatVector((1,2,3)) r = repr(vec).split('\n') assert r[-1].startswith('[') assert r[-1].endswith(']') assert len(r[-1].split(',')) == 3 @pytest.mark.parametrize( 'params', ((robjects.vectors.DataFrame, dict((('a', ri.IntSexpVector((1, 2, 3))), ('b', ri.IntSexpVector((4, 5, 6))), ('c', ri.IntSexpVector((7, 8, 9))), ('d', ri.IntSexpVector((7, 8, 9))), ('e', ri.IntSexpVector((7, 8, 9))), ))), (robjects.vectors.IntVector, (1, 2, 3, 4, 5)), (robjects.vectors.ListVector, (('a', 1), ('b', 2), ('b', 3), ('c', 4), ('d', 5))), (robjects.vectors.FloatVector, (1, 2, 3, 4, 5))) ) def test_repr_html(params): vec_cls, data = params vec = vec_cls(data) s = vec._repr_html_().split('\n') assert s[2].strip().startswith('') assert s[-2].strip().endswith('
') s = vec._repr_html_(max_items=2).split('\n') assert s[2].strip().startswith('') assert s[-2].strip().endswith('
') def test_repr_nonvectorinlist(): vec = robjects.ListVector(OrderedDict((('a', 1), ('b', robjects.Formula('y ~ x')), ))) s = repr(vec) assert s.startswith("R object with classes: ('list',) " "mapped to:\n[IntSexpVector, LangSexpVector]") def test_items(): vec = robjects.IntVector(range(3)) vec.names = robjects.StrVector('abc') names = [k for k,v in vec.items()] assert names == ['a', 'b', 'c'] values = [v for k,v in vec.items()] assert values == [0, 1, 2] def test_itemsnonames(): vec = robjects.IntVector(range(3)) names = [k for k,v in vec.items()] assert names == [None, None, None] values = [v for k,v in vec.items()] assert values == [0, 1, 2] def test_sequence_to_vector(): res = robjects.sequence_to_vector((1, 2, 3)) assert isinstance(res, robjects.IntVector) res = robjects.sequence_to_vector((1, 2, 3.0)) assert isinstance(res, robjects.FloatVector) res = robjects.sequence_to_vector(('ab', 'cd', 'ef')) assert isinstance(res, robjects.StrVector) with pytest.raises(ValueError): robjects.sequence_to_vector(list()) def test_sample(): vec = robjects.IntVector(range(100)) spl = vec.sample(10) assert len(spl) == 10 def test_sample_probabilities(): vec = robjects.IntVector(range(100)) spl = vec.sample(10, probabilities=robjects.FloatVector([.01] * 100)) assert len(spl) == 10 def test_sample_probabilities_novector(): vec = robjects.IntVector(range(100)) spl = vec.sample(10, probabilities=[.01] * 100) assert len(spl) == 10 def test_sample_probabilities_error_len(): vec = robjects.IntVector(range(100)) with pytest.raises(ValueError): vec.sample(10, probabilities=robjects.FloatVector([.01] * 10)) def test_sample_error(): vec = robjects.IntVector(range(100)) with pytest.raises(ri.embedded.RRuntimeError): spl = vec.sample(110) def test_sample_replacement(): vec = robjects.IntVector(range(100)) spl = vec.sample(110, replace=True) assert len(spl) == 110 rpy2-3.2.6/rpy2/tests/robjects/test_packages_utils.py0000644000175000017500000000506613576515767024101 0ustar laurentlaurent00000000000000import pytest import rpy2.robjects.packages_utils as p_u def test_default_symbol_r2python(): test_values = ( ('foo', 'foo'), ('foo.bar', 'foo_bar'), ('foo_bar', 'foo_bar') ) for provided, expected in test_values: assert expected == p_u.default_symbol_r2python(provided) @pytest.mark.parametrize( 'symbol_mapping,expected_conflict,expected_resolution', [({'foo_bar': ['foo.bar'], 'foo': ['foo']}, {}, {}), ({'foo_bar': ['foo.bar', 'foo_bar'], 'foo': ['foo']}, {}, {'foo_bar': ['foo_bar'], 'foo_bar_': ['foo.bar']}), ({'foo_bar': ['foo.bar', 'foo_bar', 'foo_bar_'], 'foo': ['foo']}, {'foo_bar': ['foo.bar', 'foo_bar', 'foo_bar_']}, {}), ], ) def test_default_symbol_resolve_noconflicts(symbol_mapping, expected_conflict, expected_resolution): conflicts, resolved_mapping = p_u.default_symbol_resolve(symbol_mapping) assert conflicts == expected_conflict assert resolved_mapping == expected_resolution def test___map_symbols(): rnames = ('foo.bar', 'foo_bar', 'foo') translations = {} (symbol_mapping, conflicts, resolutions) = p_u._map_symbols(rnames, translations) expected_symbol_mapping = { 'foo_bar': ['foo.bar', 'foo_bar'], 'foo': ['foo'] } for new_symbol, old_symbols in expected_symbol_mapping.items(): assert symbol_mapping[new_symbol] == old_symbols translations = {'foo.bar': 'foo_dot_bar'} (symbol_mapping, conflicts, resolutions) = p_u._map_symbols(rnames, translations) def test__fix_map_symbols_invalidonconflict(): msg_prefix = '' exception = ValueError symbol_mappings = {'foo': 'foo'} conflicts = {'foo_bar': ['foo.bar', 'foo_bar']} on_conflict = 'foo' with pytest.raises(ValueError): p_u._fix_map_symbols(symbol_mappings, conflicts, on_conflict, msg_prefix, exception) def test__fix_map_symbols_conflictwarn(): msg_prefix = '' exception = ValueError symbol_mappings = {'foo': 'foo'} conflicts = {'foo_bar': ['foo.bar', 'foo_bar']} on_conflict = 'warn' with pytest.warns(UserWarning): p_u._fix_map_symbols(symbol_mappings, conflicts, on_conflict, msg_prefix, exception) rpy2-3.2.6/rpy2/tests/robjects/test_conversion_numpy.py0000644000175000017500000001717713615570032024502 0ustar laurentlaurent00000000000000import pytest import sys from rpy2 import robjects from rpy2 import rinterface import rpy2.robjects.conversion as conversion r = robjects.r has_numpy = True try: import numpy import rpy2.robjects.numpy2ri as rpyn except: has_numpy = False @pytest.fixture() def numpy_conversion(): rpyn.activate() yield rpyn.deactivate() @pytest.mark.skipif(not has_numpy, reason='package numpy cannot be imported') @pytest.mark.usefixtures('numpy_conversion') class TestNumpyConversions(object): def test_activate(self): rpyn.deactivate() #FIXME: is the following still making sense ? assert rpyn.py2rpy != conversion.py2rpy l = len(conversion.py2rpy.registry) k = set(conversion.py2rpy.registry.keys()) rpyn.activate() assert len(conversion.py2rpy.registry) > l rpyn.deactivate() assert len(conversion.py2rpy.registry) == l assert set(conversion.py2rpy.registry.keys()) == k def test_activate_twice(self): rpyn.deactivate() #FIXME: is the following still making sense ? assert rpyn.py2rpy != conversion.py2rpy l = len(conversion.py2rpy.registry) k = set(conversion.py2rpy.registry.keys()) rpyn.activate() rpyn.deactivate() rpyn.activate() assert len(conversion.py2rpy.registry) > l rpyn.deactivate() assert len(conversion.py2rpy.registry) == l assert set(conversion.py2rpy.registry.keys()) == k def check_homogeneous(self, obj, mode, storage_mode): converted = conversion.py2rpy(obj) assert r["mode"](converted)[0] == mode assert r["storage.mode"](converted)[0] == storage_mode assert list(obj) == list(converted) assert r["is.array"](converted)[0] is True return converted def test_vector_boolean(self): l = [True, False, True] b = numpy.array(l, dtype=numpy.bool_) b_r = self.check_homogeneous(b, "logical", "logical") assert tuple(l) == tuple(b_r) def test_vector_integer(self): l = [1, 2, 3] i = numpy.array(l, dtype="i") i_r = self.check_homogeneous(i, "numeric", "integer") assert tuple(l) == tuple(i_r) def test_vector_float(self): l = [1.0, 2.0, 3.0] f = numpy.array(l, dtype="f") f_r = self.check_homogeneous(f, "numeric", "double") for orig, conv in zip(l, f_r): assert abs(orig-conv) < 0.000001 def test_vector_complex(self): l = [1j, 2j, 3j] c = numpy.array(l, dtype=numpy.complex_) c_r = self.check_homogeneous(c, "complex", "complex") for orig, conv in zip(l, c_r): assert abs(orig.real-conv.real) < 0.000001 assert abs(orig.imag-conv.imag) < 0.000001 def test_vector_unicode_character(self): l = [u"a", u"c", u"e"] u = numpy.array(l, dtype="U") u_r = self.check_homogeneous(u, "character", "character") assert tuple(l) == tuple(u_r) def test_vector_bytes(self): l = [b'a', b'b', b'c'] s = numpy.array(l, dtype = '|S1') converted = conversion.py2rpy(s) assert r["mode"](converted)[0] == 'raw' assert r["storage.mode"](converted)[0] == 'raw' assert bytearray(b''.join(l)) == bytearray(converted) def test_array(self): i2d = numpy.array([[1, 2, 3], [4, 5, 6]], dtype='i') i2d_r = conversion.py2rpy(i2d) assert r['storage.mode'](i2d_r)[0] == 'integer' assert tuple(r['dim'](i2d_r)) == (2, 3) # Make sure we got the row/column swap right: assert r['['](i2d_r, 1, 2)[0] == i2d[0, 1] f3d = numpy.arange(24, dtype='f').reshape((2, 3, 4)) f3d_r = conversion.py2rpy(f3d) assert r['storage.mode'](f3d_r)[0] == 'double' assert tuple(r['dim'](f3d_r)) == (2, 3, 4) # Make sure we got the row/column swap right: #assert r['['](f3d_r, 1, 2, 3)[0] == f3d[0, 1, 2] def test_scalar(self): i32 = numpy.int32(100) i32_r = conversion.py2rpy(i32) i32_test = numpy.array(i32_r)[0] assert i32 == i32_test i64 = numpy.int64(100) i64_r = conversion.py2rpy(i64) i64_test = numpy.array(i64_r)[0] assert i64 == i64_test @pytest.mark.skipif(not (has_numpy and hasattr(numpy, 'float128')), reason='numpy.float128 not available on this system') def test_scalar_f128(self): f128 = numpy.float128(100.000000003) f128_r = conversion.py2rpy(f128) f128_test = numpy.array(f128_r)[0] assert f128 == f128_test def test_object_array(self): o = numpy.array([1, "a", 3.2], dtype=numpy.object_) o_r = conversion.py2rpy(o) assert r['mode'](o_r)[0] == 'list' assert r['[['](o_r, 1)[0] == 1 assert r['[['](o_r, 2)[0] == 'a' assert r['[['](o_r, 3)[0] == 3.2 def test_record_array(self): rec = numpy.array([(1, 2.3), (2, -0.7), (3, 12.1)], dtype=[("count", "i"), ("value", numpy.double)]) rec_r = conversion.py2rpy(rec) assert r["is.data.frame"](rec_r)[0] is True assert tuple(r["names"](rec_r)) == ("count", "value") count_r = rec_r[rec_r.names.index('count')] value_r = rec_r[rec_r.names.index('value')] assert r["storage.mode"](count_r)[0] == 'integer' assert r["storage.mode"](value_r)[0] == 'double' assert count_r[1] == 2 assert value_r[2] == 12.1 def test_bad_array(self): u = numpy.array([1, 2, 3], dtype=numpy.uint32) with pytest.raises(ValueError): conversion.py2rpy(u) def test_assign_numpy_object(self): x = numpy.arange(-10., 10., 1) env = robjects.Environment() env['x'] = x assert len(env) == 1 # do have an R object of the right type ? x_r = env['x'] assert robjects.rinterface.RTYPES.REALSXP == x_r.typeof # assert tuple(x_r.dim) == (20,) def test_dataframe_to_numpy(self): df = robjects.vectors.DataFrame( {'a': 1, 'b': 2, 'c': robjects.vectors.FactorVector('e')}) rec = conversion.rpy2py(df) assert numpy.recarray == type(rec) assert rec.a[0] == 1 assert rec.b[0] == 2 assert rec.c[0] == 'e' def test_atomic_vector_to_numpy(self): v = robjects.vectors.IntVector((1,2,3)) a = rpyn.rpy2py(v) assert isinstance(a, numpy.ndarray) assert v[0] == 1 def test_rx2(self): df = robjects.vectors.DataFrame({ "A": robjects.vectors.IntVector([1,2,3]), "B": robjects.vectors.IntVector([1,2,3])}) b = df.rx2('B') assert tuple((1,2,3)) == tuple(b) @pytest.mark.skipif(not has_numpy, reason='package numpy cannot be imported') def test_unsignednumpyint_to_rint(): values = (1,2,3) a8 = numpy.array(values, dtype='uint8') v = rpyn.unsignednumpyint_to_rint(a8) assert values == tuple(v) a64 = numpy.array(values, dtype='uint64') with pytest.raises(ValueError): rpyn.unsignednumpyint_to_rint(a64) @pytest.mark.skipif(not has_numpy, reason='package numpy cannot be imported') @pytest.mark.parametrize('values,expected_cls', ((['a', 1, 2], robjects.vectors.ListVector), (['a', 'b', 'c'], rinterface.StrSexpVector), ([b'a', b'b', b'c'], rinterface.ByteSexpVector))) def test_numpy_O_py2rpy(values, expected_cls): a = numpy.array(values, dtype='O') v = rpyn.numpy_O_py2rpy(a) assert isinstance(v, expected_cls) rpy2-3.2.6/rpy2/tests/__init__.py0000644000175000017500000000000013576515767017747 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/ipython/0000755000175000017500000000000013615572523017325 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/ipython/test_ggplot.py0000644000175000017500000000043013615570032022220 0ustar laurentlaurent00000000000000import pytest from rpy2.robjects.vectors import DataFrame import rpy2.robjects.lib.ggplot2 from rpy2.ipython import ggplot def test_image_png(): dataf = DataFrame({'x': 1, 'Y': 2}) g = rpy2.robjects.lib.ggplot2.ggplot(dataf) img = ggplot.image_png(g) assert img rpy2-3.2.6/rpy2/tests/ipython/test_html.py0000644000175000017500000000137713576515767021727 0ustar laurentlaurent00000000000000import pytest from rpy2.robjects import vectors from rpy2.robjects.packages import importr from rpy2.ipython import html base = importr('base') @pytest.mark.parametrize( 'o,func', [(vectors.IntVector([1, 2, 3]), html.html_vector_horizontal), (vectors.FloatVector([1, 2, 3]), html.html_vector_horizontal), (vectors.StrVector(['a', 'b' 'c']), html.html_vector_horizontal), (vectors.FactorVector(['a', 'b' 'c']), html.html_vector_horizontal), (vectors.ListVector({'a': 1, 'b': 2}), html.html_rlist), (vectors.DataFrame({'a': 1, 'b': 'z'}), html.html_rdataframe), ('x <- c(1, 2, 3)', html.html_sourcecode), (base.c, html.html_ridentifiedobject)]) def test_html_func(o, func): res = func(o) assert isinstance(res, str) rpy2-3.2.6/rpy2/tests/ipython/__init__.py0000644000175000017500000000000013576515767021441 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/ipython/test_rmagic.py0000644000175000017500000002674113576515767022227 0ustar laurentlaurent00000000000000import pytest import textwrap from itertools import product import rpy2.rinterface_lib.callbacks from .. import utils # Currently numpy is a testing requirement, but rpy2 should work without numpy try: import numpy as np has_numpy = True except: has_numpy = False try: import pandas as pd has_pandas = True except: has_pandas = False from IPython.testing.globalipapp import get_ipython from io import StringIO np_string_type = 'U' from rpy2.ipython import rmagic # from IPython.core.getipython import get_ipython from rpy2 import rinterface from rpy2.robjects import r, vectors, globalenv import rpy2.robjects.packages as rpacks @pytest.fixture(scope='module') def clean_globalenv(): yield for name in rinterface.globalenv.keys(): del rinterface.globalenv[name] @pytest.fixture(scope='module') def ipython_with_magic(): ip = get_ipython() # This is just to get a minimally modified version of the changes # working ip.magic('load_ext rpy2.ipython') return ip @pytest.fixture(scope='function') def set_conversion(ipython_with_magic): if hasattr(rmagic.template_converter, 'activate'): rmagic.template_converter.activate() yield ipython_with_magic # This seems like the safest thing to return to a safe state ipython_with_magic.run_line_magic('Rdevice', 'png') if hasattr(rmagic.template_converter, 'deactivate'): rmagic.template_converter.deactivate() def test_RInterpreterError(): line = 123 err = 'Arrh!' stdout = 'Kaput' rie = rmagic.RInterpreterError(line, err, stdout) assert str(rie).startswith(rie.msg_prefix_template % (line, err)) @pytest.mark.skipif(not has_numpy, reason='numpy not installed') def test_push(ipython_with_magic, clean_globalenv): ipython_with_magic.push({'X':np.arange(5), 'Y':np.array([3,5,4,6,7])}) ipython_with_magic.run_line_magic('Rpush', 'X Y') np.testing.assert_almost_equal(np.asarray(r('X')), ipython_with_magic.user_ns['X']) np.testing.assert_almost_equal(np.asarray(r('Y')), ipython_with_magic.user_ns['Y']) @pytest.mark.skipif(not has_numpy, reason='numpy not installed') def test_push_localscope(ipython_with_magic, clean_globalenv): """Test that Rpush looks for variables in the local scope first.""" ipython_with_magic.run_cell( textwrap.dedent( """ def rmagic_addone(u): %Rpush u %R result = u+1 %Rpull result return result[0] u = 0 result = rmagic_addone(12344) """) ) result = ipython_with_magic.user_ns['result'] np.testing.assert_equal(result, 12345) @pytest.mark.skipif(not has_pandas, reason='pandas is not available in python') @pytest.mark.skipif(not has_numpy, reason='numpy not installed') def test_push_dataframe(ipython_with_magic, clean_globalenv): df = pd.DataFrame([{'a': 1, 'b': 'bar'}, {'a': 5, 'b': 'foo', 'c': 20}]) ipython_with_magic.push({'df':df}) ipython_with_magic.run_line_magic('Rpush', 'df') # This is converted to factors, which are currently converted back to Python # as integers, so for now we test its representation in R. sio = StringIO() with utils.obj_in_module(rpy2.rinterface_lib.callbacks, 'consolewrite_print', sio.write): r('print(df$b[1])') assert '[1] "bar"' in sio.getvalue() # Values come packaged in arrays, so we unbox them to test. assert r('df$a[2]')[0] == 5 missing = r('df$c[1]')[0] assert np.isnan(missing), missing @pytest.mark.skipif(not has_numpy, reason='numpy not installed') def test_pull(ipython_with_magic, clean_globalenv): r('Z=c(11:20)') ipython_with_magic.run_line_magic('Rpull', 'Z') np.testing.assert_almost_equal(np.asarray(r('Z')), ipython_with_magic.user_ns['Z']) np.testing.assert_almost_equal(ipython_with_magic.user_ns['Z'], np.arange(11,21)) @pytest.mark.skipif(not has_numpy, reason='numpy not installed') def test_Rconverter(ipython_with_magic, clean_globalenv): # If we get to dropping numpy requirement, we might use something # like the following: # assert tuple(buffer(a).buffer_info()) == tuple(buffer(b).buffer_info()) # numpy recarray (numpy's version of a data frame) dataf_np= np.array([(1, 2.9, 'a'), (2, 3.5, 'b'), (3, 2.1, 'c')], dtype=[('x', ' rinterface-object mapper def test_globalenv(): assert isinstance(rinterface.globalenv, rinterface.SexpEnvironment) def test_getitem(): with pytest.raises(KeyError): rinterface.globalenv['help'] assert isinstance(rinterface.globalenv.find('help'), rinterface.Sexp) def test_getitem_invalid(): env = rinterface.baseenv["new.env"]() with pytest.raises(TypeError): env[None] with pytest.raises(ValueError): env[''] def test_setitem_invalid(): env = rinterface.baseenv["new.env"]() with pytest.raises(TypeError): env[None] = 0 with pytest.raises(ValueError): env[''] = 0 def test_frame(): env = rinterface.baseenv["new.env"]() f = env.frame() # Outside of an R call stack a frame will be NULL, # or so I understand. assert f is rinterface.NULL def test_find_invalid_notstring(): with pytest.raises(TypeError): rinterface.globalenv.find(None) def test_find_invalid_empty(): with pytest.raises(ValueError): rinterface.globalenv.find('') def test_find_invalid_notfound(): with pytest.raises(KeyError): rinterface.globalenv.find('asdf') def test_find_closure(): help_R = rinterface.globalenv.find('help') assert isinstance(help_R, rinterface.SexpClosure) def test_find_vector(): pi_R = rinterface.globalenv.find('pi') assert isinstance(pi_R, rinterface.SexpVector) def test_find_environment(): ge_R = rinterface.globalenv.find(".GlobalEnv") assert isinstance(ge_R, rinterface.SexpEnvironment) def test_find_onlyfromloadedlibrary(): with pytest.raises(KeyError): rinterface.globalenv.find('survfit') try: rinterface.evalr('library("survival")') sfit_R = rinterface.globalenv.find('survfit') assert isinstance(sfit_R, rinterface.SexpClosure) finally: rinterface.evalr('detach("package:survival")') def test_find_functiononly_keyerror(): # now with the function-only option with pytest.raises(KeyError): rinterface.globalenv.find('pi', wantfun=True) def test_find_functiononly(): hist = rinterface.globalenv.find('hist', wantfun=False) assert rinterface.RTYPES.CLOSXP == hist.typeof rinterface.globalenv['hist'] = rinterface.StrSexpVector(['foo', ]) with pytest.raises(KeyError): rinterface.globalenv.find('hist', wantfun=True) # TODO: isn't this already tested elsewhere ? def test_subscript_emptystring(): ge = rinterface.globalenv with pytest.raises(ValueError): ge[''] def test_subscript(): ge = rinterface.globalenv obj = ge.find('letters') ge['a'] = obj a = ge['a'] assert ge.find('identical')(obj, a) def test_subscript_utf8(): env = rinterface.baseenv['new.env']() env['呵呵'] = rinterface.IntSexpVector((1,)) assert len(env) == 1 assert len(env['呵呵']) == 1 assert env['呵呵'][0] == 1 def test_subscript_missing_utf8(): env = rinterface.baseenv['new.env']() with pytest.raises(KeyError),\ pytest.warns(rinterface.RRuntimeWarning): env['呵呵'] def test_length(): new_env = rinterface.globalenv.find('new.env') env = new_env() assert len(env) == 0 env['a'] = rinterface.IntSexpVector([123, ]) assert len(env) == 1 env['b'] = rinterface.IntSexpVector([123, ]) assert len(env) == 2 def test_iter(): new_env = rinterface.globalenv.find('new.env') env = new_env() env['a'] = rinterface.IntSexpVector([123, ]) env['b'] = rinterface.IntSexpVector([456, ]) symbols = [x for x in env] assert len(symbols) == 2 for s in ['a', 'b']: assert s in symbols def test_keys(): new_env = rinterface.globalenv.find('new.env') env = new_env() env['a'] = rinterface.IntSexpVector([123, ]) env['b'] = rinterface.IntSexpVector([456, ]) symbols = tuple(env.keys()) assert len(symbols) == 2 for s in ['a', 'b']: assert s in symbols def test_del(): env = rinterface.globalenv.find("new.env")() env['a'] = rinterface.IntSexpVector([123, ]) env['b'] = rinterface.IntSexpVector([456, ]) assert len(env) == 2 del(env['a']) assert len(env) == 1 assert 'b' in env def test_del_keyerror(): with pytest.raises(KeyError): rinterface.globalenv.__delitem__('foo') def test_del_baseerror(): with pytest.raises(ValueError): rinterface.baseenv.__delitem__('letters') rpy2-3.2.6/rpy2/tests/rinterface/test_externalptr.py0000644000175000017500000000301013576515767023745 0ustar laurentlaurent00000000000000import pytest from .. import utils import rpy2.rinterface as rinterface rinterface.initr() def _just_pass(x): pass @pytest.fixture(scope='module') def silent_console_print(): with utils.obj_in_module(rinterface.callbacks, 'consolewrite_print', _just_pass): yield def test_from_pyobject(): pyobject = 'ahaha' sexp_new = rinterface.SexpExtPtr.from_pyobject(pyobject) # R External pointers are never copied. assert rinterface.RTYPES.EXTPTRSXP == sexp_new.typeof def test_from_pyobject_new_tag(): pyobject = 'ahaha' sexp_new = (rinterface.SexpExtPtr .from_pyobject(pyobject, tag='b')) assert sexp_new.typeof == rinterface.RTYPES.EXTPTRSXP assert sexp_new.TYPE_TAG == 'b' def test_from_pyobject_invalid_tag(): pyobject = 'ahaha' with pytest.raises(TypeError): rinterface.SexpExtPtr.from_pyobject(pyobject, tag=True) @pytest.mark.skip(reason='WIP') def test_from_pyobject_protected(): pyobject = 'ahaha' sexp_new = (rinterface.SexpExtPtr .from_pyobject(pyobject, protected=rinterface.StrSexpVector("c"))) assert sexp_new.typeof == rinterface.RTYPES.EXTPTRSXP assert sexp_new.__protected__[0] == 'c' @pytest.mark.skip(reason='WIP') def test_from_pyobject_invalid_protected(): pyobject = 'ahaha' with pytest.raises(TypeError): rinterface.SexpExtPtr.from_pyobject(pyobject, protected=True) rpy2-3.2.6/rpy2/tests/rinterface/test_functions.py0000644000175000017500000001053413576515767023416 0ustar laurentlaurent00000000000000# coding: utf-8 import pytest import rpy2.rinterface as rinterface import rpy2.rlike.container as rlc rinterface.initr() def _noconsole(x): pass @pytest.fixture(scope='module') def silent_consolewrite(): module = rinterface.callbacks name = 'consolewrite_print' backup_func = getattr(module, name) setattr(module, name, _noconsole) try: yield finally: setattr(module, name, backup_func) def test_new(): x = 'a' with pytest.raises(ValueError): rinterface.SexpClosure(x) def test_typeof(): sexp = rinterface.globalenv.find('plot') assert sexp.typeof == rinterface.RTYPES.CLOSXP def test_r_error(): r_sum = rinterface.baseenv['sum'] letters = rinterface.baseenv['letters'] with pytest.raises(rinterface.embedded.RRuntimeError),\ pytest.warns(rinterface.RRuntimeWarning): r_sum(letters) def test_string_argument(): r_nchar = rinterface.baseenv['::']('base', 'nchar') res = r_nchar('foo') assert res[0] == 3 def test_utf8_argument_name(): c = rinterface.globalenv.find('c') d = dict([(u'哈哈', 1)]) res = c(**d) assert u'哈哈' == res.do_slot('names')[0] def test_emptystringparams(): d = dict([('', 1)]) with pytest.raises(ValueError): rinterface.baseenv['list'](**d) def test_closureenv_isenv(): exp = rinterface.parse('function() { }') fun = rinterface.baseenv['eval'](exp) assert isinstance(fun.closureenv, rinterface.SexpEnvironment) def test_closureenv(): assert 'y' not in rinterface.globalenv exp = rinterface.parse('function(x) { x[y] }') fun = rinterface.baseenv['eval'](exp) vec = rinterface.baseenv['letters'] assert isinstance(fun.closureenv, rinterface.SexpEnvironment) with pytest.raises(rinterface.embedded.RRuntimeError): with pytest.warns(rinterface.RRuntimeWarning): fun(vec) fun.closureenv['y'] = (rinterface .IntSexpVector([1, ])) assert 'a' == fun(vec)[0] fun.closureenv['y'] = (rinterface .IntSexpVector([2, ])) assert 'b' == fun(vec)[0] def test_call_s4_setClass(): # R's package "methods" can perform uncommon operations r_setClass = rinterface.globalenv.find('setClass') r_representation = rinterface.globalenv.find('representation') attrnumeric = rinterface.StrSexpVector(['numeric', ]) classname = rinterface.StrSexpVector(['Track', ]) classrepr = r_representation(x=attrnumeric, y=attrnumeric) r_setClass(classname, classrepr) # TODO: where is the test ? def test_call_OrdDict(): ad = rlc.OrdDict((('a', rinterface.IntSexpVector([2, ])), ('b', rinterface.IntSexpVector([1, ])), (None, rinterface.IntSexpVector([5, ])), ('c', rinterface.IntSexpVector([0, ])))) mylist = rinterface.baseenv['list'].rcall(tuple(ad.items()), rinterface.globalenv) names = [x for x in mylist.do_slot('names')] for i in range(4): assert ('a', 'b', '', 'c')[i] == names[i] def test_call_OrdDictEnv(): ad = rlc.OrdDict(((None, rinterface.parse('sum(x)')), )) env_a = rinterface.baseenv['new.env']() env_a['x'] = rinterface.IntSexpVector([1, 2, 3]) sum_a = rinterface.baseenv['eval'].rcall(tuple(ad.items()), env_a) assert 6 == sum_a[0] env_b = rinterface.baseenv['new.env']() env_b['x'] = rinterface.IntSexpVector([4, 5, 6]) sum_b = rinterface.baseenv['eval'].rcall(tuple(ad.items()), env_b) assert 15 == sum_b[0] def test_error_in_call(): r_sum = rinterface.baseenv['sum'] with pytest.raises(rinterface.embedded.RRuntimeError),\ pytest.warns(rinterface.RRuntimeWarning): r_sum(2, 'a') def test_missing_arg(): exp = rinterface.parse('function(x) { missing(x) }') fun = rinterface.baseenv['eval'](exp) nonmissing = rinterface.IntSexpVector([0, ]) missing = rinterface.MissingArg assert not fun(nonmissing)[0] assert fun(missing)[0] def test_scalar_convert_integer(): assert 'integer' == rinterface.baseenv['typeof'](int(1))[0] def test_scalar_convert_double(): assert 'double' == rinterface.baseenv['typeof'](1.0)[0] def test_scalar_convert_boolean(): assert 'logical' == rinterface.baseenv['typeof'](True)[0] rpy2-3.2.6/rpy2/tests/rinterface/__init__.py0000644000175000017500000000000013576515767022071 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/tests/rinterface/test_sexp.py0000644000175000017500000001645113576515767022371 0ustar laurentlaurent00000000000000import copy import gc import pytest import rpy2.rinterface as rinterface rinterface.initr() def test_invalid_init(): with pytest.raises(ValueError): rinterface.Sexp('a') def test_init_from_existing(): sexp = rinterface.baseenv.find('letters') sexp_new = rinterface.Sexp(sexp) assert sexp_new._sexpobject is sexp._sexpobject def test_typeof(): assert isinstance(rinterface.baseenv.typeof, int) def test_get(): sexp = rinterface.baseenv.find('letters') assert sexp.typeof == rinterface.RTYPES.STRSXP sexp = rinterface.baseenv.find('pi') assert sexp.typeof == rinterface.RTYPES.REALSXP sexp = rinterface.baseenv.find('options') assert sexp.typeof == rinterface.RTYPES.CLOSXP @pytest.mark.parametrize('cls', (rinterface.IntSexpVector, rinterface.ListSexpVector)) def test_list_attrs(cls): x = cls((1, 2, 3)) assert len(x.list_attrs()) == 0 x.do_slot_assign('a', rinterface.IntSexpVector((33, ))) assert len(x.list_attrs()) == 1 assert 'a' in x.list_attrs() def test_do_slot(): sexp = rinterface.baseenv.find('.Platform') names = sexp.do_slot('names') assert len(names) > 1 assert 'OS.type' in names def test_names(): sexp = rinterface.baseenv.find('.Platform') names = sexp.names assert len(names) > 1 assert 'OS.type' in names def test_names_set(): sexp = rinterface.IntSexpVector([1, 2, 3]) assert sexp.names.rid == rinterface.NULL.rid sexp.names = rinterface.StrSexpVector(['a', 'b', 'c']) assert len(sexp.names) > 1 assert tuple(sexp.names) == ('a', 'b', 'c') def test_names_set_invalid(): sexp = rinterface.IntSexpVector([1, 2, 3]) assert sexp.names.rid == rinterface.NULL.rid with pytest.raises(ValueError): sexp.names = ('a', 'b', 'c') def test_do_slot_missing(): sexp = rinterface.baseenv.find('pi') with pytest.raises(LookupError): sexp.do_slot('foo') def test_do_slot_not_string(): sexp = rinterface.baseenv.find('pi') with pytest.raises(ValueError): sexp.do_slot(None) def test_do_slot_empty_string(): sexp = rinterface.baseenv.find('pi') with pytest.raises(ValueError): sexp.do_slot('') def test_do_slot_assign_create(): sexp = rinterface.IntSexpVector([]) slot_value = rinterface.IntSexpVector([3, ]) sexp.do_slot_assign('foo', slot_value) slot_value_back = sexp.do_slot('foo') assert len(slot_value_back) == len(slot_value) assert all(x == y for x, y in zip(slot_value, slot_value_back)) def test_do_slot_reassign(): sexp = rinterface.IntSexpVector([]) slot_value_a = rinterface.IntSexpVector([3, ]) sexp.do_slot_assign('foo', slot_value_a) slot_value_b = rinterface.IntSexpVector([5, 6]) sexp.do_slot_assign('foo', slot_value_b) slot_value_back = sexp.do_slot('foo') assert len(slot_value_b) == len(slot_value_back) assert all(x == y for x, y in zip(slot_value_b, slot_value_back)) def test_do_slot_assign_empty_string(): sexp = rinterface.IntSexpVector([]) slot_value = rinterface.IntSexpVector([3, ]) with pytest.raises(ValueError): sexp.do_slot_assign('', slot_value) def test_sexp_rsame_true(): sexp_a = rinterface.baseenv.find("letters") sexp_b = rinterface.baseenv.find("letters") assert sexp_a.rsame(sexp_b) def test_sexp_rsame_false(): sexp_a = rinterface.baseenv.find("letters") sexp_b = rinterface.baseenv.find("pi") assert not sexp_a.rsame(sexp_b) def test_sexp_rsame_invalid(): sexp_a = rinterface.baseenv.find("letters") with pytest.raises(ValueError): sexp_a.rsame('foo') def test___sexp__(): sexp = rinterface.IntSexpVector([1, 2, 3]) sexp_count = sexp.__sexp_refcount__ sexp_cobj = sexp.__sexp__ d = dict(rinterface._rinterface.protected_rids()) assert sexp_count == d[sexp.rid] assert sexp_count == sexp.__sexp_refcount__ sexp2 = rinterface.IntSexpVector([4, 5, 6, 7]) sexp2_rid = sexp2.rid sexp2.__sexp__ = sexp_cobj del(sexp) gc.collect() d = dict(rinterface._rinterface.protected_rids()) assert d.get(sexp2_rid) is None def test_rclass_get(): sexp = rinterface.baseenv.find('character')(1) assert len(sexp.rclass) == 1 assert sexp.rclass[0] == 'character' sexp = rinterface.baseenv.find('matrix')(0) assert len(sexp.rclass) == 1 assert sexp.rclass[0] == 'matrix' sexp = rinterface.baseenv.find('array')(0) assert len(sexp.rclass) == 1 assert sexp.rclass[0] == 'array' sexp = rinterface.baseenv.find('new.env')() assert len(sexp.rclass) == 1 assert sexp.rclass[0] == 'environment' def test_rclass_set(): sexp = rinterface.IntSexpVector([1, 2, 3]) sexp.rclass = rinterface.StrSexpVector(['foo']) assert len(sexp.rclass) == 1 assert sexp.rclass[0] == 'foo' sexp.rclass = 'bar' assert len(sexp.rclass) == 1 assert sexp.rclass[0] == 'bar' def test_rclass_set_invalid(): sexp = rinterface.IntSexpVector([1, 2, 3]) with pytest.raises(TypeError): sexp.rclass = rinterface.StrSexpVector(123) def test__sexp__wrongtypeof(): sexp = rinterface.IntSexpVector([1, 2, 3]) cobj = sexp.__sexp__ sexp = rinterface.StrSexpVector(['a', 'b']) assert len(sexp) == 2 with pytest.raises(ValueError): sexp.__sexp__ = cobj def test__sexp__set(): x = rinterface.IntSexpVector([1, 2, 3]) x_s = x.__sexp__ x_rid = x.rid # The Python reference count of the capsule is incremented, # not the rpy2 reference count assert x.__sexp_refcount__ == 1 y = rinterface.IntSexpVector([4, 5, 6]) y_count = y.__sexp_refcount__ y_rid = y.rid assert y_count == 1 assert x_rid in [elt[0] for elt in rinterface._rinterface.protected_rids()] x.__sexp__ = y.__sexp__ # x_s is still holding a refcount to the capsule assert x_rid in [elt[0] for elt in rinterface._rinterface.protected_rids()] # when gone, the capsule will be collected and the id no longer preserved del(x_s) assert x_rid not in [elt[0] for elt in rinterface._rinterface.protected_rids()] assert x.rid == y.rid assert y_rid == y.rid @pytest.mark.xfail(reason='WIP') def test_deepcopy(): sexp = rinterface.IntSexpVector([1, 2, 3]) assert sexp.named == 0 rinterface.baseenv.find("identity")(sexp) assert sexp.named >= 2 sexp2 = sexp.__deepcopy__() assert sexp.typeof == sexp2.typeof assert list(sexp) == list(sexp2) assert not sexp.rsame(sexp2) assert sexp2.named == 0 # should be the same as above, but just in case: sexp3 = copy.deepcopy(sexp) assert sexp.typeof == sexp3.typeof assert list(sexp) == list(sexp3) assert not sexp.rsame(sexp3) assert sexp3.named == 0 def test_rid(): globalenv_id = rinterface.baseenv.find('.GlobalEnv').rid assert globalenv_id == rinterface.globalenv.rid def test_NULL_nonzero(): assert not rinterface.NULL def test_charsxp_encoding(): encoding = rinterface.NA_Character.encoding assert encoding == rinterface.sexp.CETYPE.CE_NATIVE def test_charsxp_nchar(): v = rinterface.StrSexpVector(['abc', 'de', '']) cs = v.get_charsxp(0) assert cs.nchar() == 3 cs = v.get_charsxp(1) assert cs.nchar() == 2 cs = v.get_charsxp(2) assert cs.nchar() == 0 def test_missingtype(): assert not rinterface.MissingArg rpy2-3.2.6/rpy2/tests/rinterface/test_vector_list.py0000644000175000017500000000414013576515767023737 0ustar laurentlaurent00000000000000import pytest from .. import utils import rpy2.rinterface as ri ri.initr() def test_init_from_seq(): seq = (ri.FloatSexpVector([1.0]), ri.IntSexpVector([2, 3]), ri.StrSexpVector(['foo', 'bar'])) v = ri.ListSexpVector(seq) assert len(v) == 3 for x, y in zip(seq, v): utils.assert_equal_sequence(x, y) def test_init_From_seq_invalid_elt(): seq = (ri.FloatSexpVector([1.0]), lambda x: x, ri.StrSexpVector(['foo', 'bar'])) with pytest.raises(Exception): ri.ListSexpVector(seq) def test_getitem(): seq = (ri.FloatSexpVector([1.0]), ri.IntSexpVector([2, 3]), ri.StrSexpVector(['foo', 'bar'])) vec = ri.ListSexpVector(seq) utils.assert_equal_sequence(vec[1], ri.IntSexpVector([2, 3])) with pytest.raises(TypeError): vec[(2, 3)] def test_setitem(): seq = (ri.FloatSexpVector([1.0]), ri.IntSexpVector([2, 3]), ri.StrSexpVector(['foo', 'bar'])) vec = ri.ListSexpVector(seq) vec[1] = ri.BoolSexpVector([True, True, False]) utils.assert_equal_sequence(vec[1], ri.BoolSexpVector([True, True, False])) with pytest.raises(TypeError): vec[(2, 3)] = 123 def test_getslice(): seq = (ri.FloatSexpVector([1.0]), ri.IntSexpVector([2, 3]), ri.StrSexpVector(['foo', 'bar'])) vec = ri.ListSexpVector(seq) vec_s = vec[0:2] assert len(vec_s) == 2 utils.assert_equal_sequence(vec_s[0], ri.FloatSexpVector([1.0])) utils.assert_equal_sequence(vec_s[1], ri.IntSexpVector([2, 3])) def test_setslice(): seq = (ri.FloatSexpVector([1.0]), ri.IntSexpVector([2, 3]), ri.StrSexpVector(['foo', 'bar'])) vec = ri.ListSexpVector(seq) vec[0:2] = ri.ListSexpVector( [ri.FloatSexpVector([10.0]), ri.IntSexpVector([20, 30])] ) assert len(vec) == 3 utils.assert_equal_sequence(vec[0], ri.FloatSexpVector([10.0])) utils.assert_equal_sequence(vec[1], ri.IntSexpVector([20, 30])) utils.assert_equal_sequence(vec[2], ri.StrSexpVector(['foo', 'bar'])) rpy2-3.2.6/rpy2/tests/rinterface/test_bufferprotocol.py0000644000175000017500000000175413576515767024445 0ustar laurentlaurent00000000000000import rpy2.rinterface as rinterface from rpy2.rinterface import bufferprotocol rinterface.initr() def test_getrank(): v = rinterface.IntSexpVector([1, 2, 3]) assert bufferprotocol.getrank(v.__sexp__._cdata) == 1 m = rinterface.baseenv.find('matrix')(nrow=2, ncol=2) assert bufferprotocol.getrank(m.__sexp__._cdata) == 2 def test_getshape(): v = rinterface.IntSexpVector([1, 2, 3]) assert bufferprotocol.getshape(v.__sexp__._cdata, 1) == (3, ) m = rinterface.baseenv.find('matrix')(nrow=2, ncol=3) assert bufferprotocol.getshape(m.__sexp__._cdata, 2) == (2, 3) def test_getstrides(): v = rinterface.IntSexpVector([1, 2, 3]) assert bufferprotocol.getstrides(v.__sexp__._cdata, [3], 8) == (8, ) m = rinterface.baseenv.find('matrix')(nrow=2, ncol=3) shape = (2, 3) sizeof = 8 # 1, 3, 5 # 2, 4, 6 expected = (sizeof, shape[0] * sizeof) assert (bufferprotocol .getstrides(m.__sexp__._cdata, shape, sizeof)) == expected rpy2-3.2.6/rpy2/tests/rinterface/test_noinitialization.py0000644000175000017500000000142413615570032024744 0ustar laurentlaurent00000000000000import pytest from rpy2 import rinterface from rpy2.rinterface import embedded @pytest.mark.skipif(embedded.rpy2_embeddedR_isinitialized, reason='Can only be tested before R is initialized.') def test_set_initoptions(): options = ('--foo', '--bar') default_options = embedded.get_initoptions() assert default_options != options try: embedded.set_initoptions(options) assert embedded.get_initoptions() == options finally: embedded.set_initoptions(default_options) @pytest.mark.skipif(embedded.rpy2_embeddedR_isinitialized, reason='Can only be tested before R is initialized.') def test_assert_isready(): with pytest.raises(embedded.RNotReadyError): embedded.assert_isready() rpy2-3.2.6/rpy2/tests/rinterface/test_vector_pairlist.py0000644000175000017500000000305513576515767024617 0ustar laurentlaurent00000000000000import rpy2.rinterface as ri ri.initr() def test_init_from_r(): pairlist = ri.baseenv.find('pairlist') pl = pairlist(a=ri.StrSexpVector(['1', ]), b=ri.StrSexpVector(['3', ])) assert pl.typeof == ri.RTYPES.LISTSXP def test_names(): pairlist = ri.baseenv.find('pairlist') pl = pairlist(a=ri.StrSexpVector(['1', ]), b=ri.StrSexpVector(['3', ])) assert tuple(pl.names) == ('a', 'b') def test_getitem_pairlist(): pairlist = ri.baseenv.find('pairlist') pl = pairlist(a=ri.StrSexpVector(['1', ]), b=ri.StrSexpVector(['3', ])) # R's behaviour is that subsetting returns an R list y = pl[0] assert y.typeof == ri.RTYPES.VECSXP assert len(y) == 1 assert y[0][0] == '1' assert y.names[0] == 'a' def test_getslice_pairlist(): pairlist = ri.baseenv.find('pairlist') vec = pairlist(a=ri.StrSexpVector(['1', ]), b=ri.StrSexpVector(['3', ]), c=ri.StrSexpVector(['6', ])) vec_slice = vec[0:2] assert vec_slice.typeof == ri.RTYPES.LISTSXP assert len(vec_slice) == 2 assert tuple(vec_slice[0][0]) == ('1', ) assert tuple(vec_slice[1][0]) == ('3', ) assert tuple(vec_slice.names) == ('a', 'b') def test_getslice_pairlist_issue380(): # Checks that root of issue #380 is fixed vec = ri.baseenv['.Options'] vec_slice = vec[0:2] assert len(vec_slice) == 2 assert vec_slice.typeof == ri.RTYPES.LISTSXP assert vec.names[0] == vec_slice.names[0] assert vec.names[1] == vec_slice.names[1] rpy2-3.2.6/rpy2/tests/rinterface/test_callbacks.py0000644000175000017500000002366713615570032023314 0ustar laurentlaurent00000000000000from .. import utils import builtins import io import logging import os import pytest import tempfile import sys import rpy2.rinterface as rinterface from rpy2.rinterface_lib import callbacks from rpy2.rinterface_lib import openrlib rinterface.initr() def test_consolewrite_print(): tmp_file = io.StringIO() stdout = sys.stdout sys.stdout = tmp_file try: callbacks.consolewrite_print('haha') finally: sys.stdout = stdout tmp_file.flush() tmp_file.seek(0) assert 'haha' == ''.join(s for s in tmp_file).rstrip() tmp_file.close() def test_set_consolewrite_print(): def make_callback(): buf = [] def f(x): nonlocal buf buf.append(x) return f f = make_callback() with utils.obj_in_module(callbacks, 'consolewrite_print', f): code = rinterface.StrSexpVector(["3", ]) rinterface.baseenv["print"](code) buf = f.__closure__[0].cell_contents assert '[1] "3"\n' == ''.join(buf) def test_consolewrite_print_error(caplog): msg = "Doesn't work." def f(x): raise Exception(msg) with utils.obj_in_module(callbacks, 'consolewrite_print', f),\ caplog.at_level(logging.ERROR, logger='callbacks.logger'): code = rinterface.StrSexpVector(["3", ]) caplog.clear() rinterface.baseenv["print"](code) assert len(caplog.record_tuples) > 0 for x in caplog.record_tuples: assert x == ('rpy2.rinterface_lib.callbacks', logging.ERROR, (callbacks ._WRITECONSOLE_EXCEPTION_LOG % msg)) def testSetResetConsole(): def make_callback(): reset = 0 def f(): nonlocal reset reset += 1 return f f = make_callback() with utils.obj_in_module(callbacks, 'consolereset', f): callbacks._consolereset() assert f.__closure__[0].cell_contents == 1 def test_resetconsole_error(caplog): error_msg = "Doesn't work." def f(): raise Exception(error_msg) with utils.obj_in_module(callbacks, 'consolereset', f),\ caplog.at_level(logging.ERROR, logger='callbacks.logger'): caplog.clear() callbacks._consolereset() assert len(caplog.record_tuples) > 0 for x in caplog.record_tuples: assert x == ('rpy2.rinterface_lib.callbacks', logging.ERROR, (callbacks ._RESETCONSOLE_EXCEPTION_LOG % error_msg)) def test_flushconsole(): def make_callback(): count = 0 def f(): nonlocal count count += 1 return f f = make_callback() with utils.obj_in_module(callbacks, 'consoleflush', f): assert f.__closure__[0].cell_contents == 0 rinterface.globalenv.find('flush.console')() assert f.__closure__[0].cell_contents == 1 def test_flushconsole_with_error(caplog): msg = "Doesn't work." def f(): raise Exception(msg) with utils.obj_in_module(callbacks, 'consoleflush', f),\ caplog.at_level(logging.ERROR, logger='callbacks.logger'): caplog.clear() rinterface.globalenv.find('flush.console')() assert len(caplog.record_tuples) > 0 for x in caplog.record_tuples: assert x == ('rpy2.rinterface_lib.callbacks', logging.ERROR, (callbacks ._FLUSHCONSOLE_EXCEPTION_LOG % msg)) def test_consoleread(): msg = 'yes' def sayyes(prompt): return msg with utils.obj_in_module(callbacks, 'consoleread', sayyes): prompt = openrlib.ffi.new('char []', b'foo') n = 1000 buf = openrlib.ffi.new('char [%i]' % n) res = callbacks._consoleread(prompt, buf, n, 0) assert res == 1 assert msg == openrlib.ffi.string(buf).decode('utf-8') def test_consoleread_empty(): def sayyes(prompt): return '' with utils.obj_in_module(callbacks, 'consoleread', sayyes): prompt = openrlib.ffi.new('char []', b'foo') n = 1000 buf = openrlib.ffi.new('char [%i]' % n) res = callbacks._consoleread(prompt, buf, n, 0) assert res == 0 assert len(openrlib.ffi.string(buf).decode('utf-8')) == 0 def test_console_read_with_error(caplog): msg = "Doesn't work." def f(prompt): raise Exception(msg) with utils.obj_in_module(callbacks, 'consoleread', f),\ caplog.at_level(logging.ERROR, logger='callbacks.logger'): caplog.clear() prompt = openrlib.ffi.new('char []', b'foo') n = 1000 buf = openrlib.ffi.new('char [%i]' % n) res = callbacks._consoleread(prompt, buf, n, 0) assert res == 0 assert len(caplog.record_tuples) > 0 for x in caplog.record_tuples: assert x == ('rpy2.rinterface_lib.callbacks', logging.ERROR, (callbacks ._READCONSOLE_EXCEPTION_LOG % msg)) def test_showmessage_default(capsys): buf = 'foo' callbacks.showmessage(buf) captured = capsys.readouterr() assert captured.out.split(os.linesep)[1] == buf def test_show_message(): def make_callback(): count = 0 def f(message): nonlocal count count += 1 return f f = make_callback() with utils.obj_in_module(callbacks, 'showmessage', f): assert f.__closure__[0].cell_contents == 0 msg = openrlib.ffi.new('char []', b'foo') callbacks._showmessage(msg) assert f.__closure__[0].cell_contents == 1 def test_show_message_with_error(caplog): error_msg = "Doesn't work." def f(message): raise Exception(error_msg) with utils.obj_in_module(callbacks, 'showmessage', f),\ caplog.at_level(logging.ERROR, logger='callbacks.logger'): caplog.clear() msg = openrlib.ffi.new('char []', b'foo') callbacks._showmessage(msg) assert len(caplog.record_tuples) > 0 for x in caplog.record_tuples: assert x == ('rpy2.rinterface_lib.callbacks', logging.ERROR, (callbacks ._SHOWMESSAGE_EXCEPTION_LOG % error_msg)) def test_choosefile_default(): inputvalue = 'foo' with utils.obj_in_module(builtins, 'input', lambda x: inputvalue): assert callbacks.choosefile('foo') == inputvalue def test_choosefile(): me = "me" def chooseMe(new): return me with utils.obj_in_module(callbacks, 'choosefile', chooseMe): res = rinterface.baseenv['file.choose']() assert me == res[0] def test_choosefile_error(): def f(prompt): raise Exception("Doesn't work.") with utils.obj_in_module(callbacks, 'consolewrite_print', utils.noconsole): with utils.obj_in_module(callbacks, 'choosefile', f): with pytest.raises(rinterface.embedded.RRuntimeError): with pytest.warns(rinterface.RRuntimeWarning): rinterface.baseenv["file.choose"]() def test_showfiles_default(capsys): filenames = (tempfile.NamedTemporaryFile(), ) filenames[0].write(b'abc') filenames[0].flush() headers = ('', ) wtitle = '' pager = '' captured = capsys.readouterr() callbacks.showfiles(tuple(x.name for x in filenames), headers, wtitle, pager) captured.out.endswith('---') def test_showfiles(): sf = [] def f(filenames, headers, wtitle, pager): sf.append(wtitle) for tf in filenames: sf.append(tf) with utils.obj_in_module(callbacks, 'showfiles', f): file_path = rinterface.baseenv['file.path'] r_home = rinterface.baseenv['R.home'] filename = file_path(r_home(rinterface.StrSexpVector(('doc', ))), rinterface.StrSexpVector(('COPYRIGHTS', ))) rinterface.baseenv['file.show'](filename) assert filename[0] == sf[1] assert 'R Information' == sf[0] def test_showfiles_error(caplog): msg = "Doesn't work." def f(filenames, headers, wtitle, pager): raise Exception(msg) with utils.obj_in_module(callbacks, 'showfiles', f),\ caplog.at_level(logging.ERROR, logger='callbacks.logger'): file_path = rinterface.baseenv['file.path'] r_home = rinterface.baseenv['R.home'] filename = file_path(r_home(rinterface.StrSexpVector(('doc', ))), rinterface.StrSexpVector(('COPYRIGHTS', ))) caplog.clear() rinterface.baseenv['file.show'](filename) assert len(caplog.record_tuples) > 0 for x in caplog.record_tuples: assert x == ('rpy2.rinterface_lib.callbacks', logging.ERROR, (callbacks ._SHOWFILE_EXCEPTION_LOG % msg)) @pytest.mark.skip(reason='WIP (should be run from worker process).') def test_cleanup(): def f(saveact, status, runlast): return None with utils.obj_in_module(callbacks, 'cleanup', f): r_quit = rinterface.baseenv['q'] with pytest.raises(rinterface.embedded.RRuntimeError): r_quit() def test_busy(): busylist = [] def busy(which): busylist.append(which) with utils.obj_in_module(callbacks, 'busy', busy): which = 1 callbacks._busy(which) assert tuple(busylist) == (1,) def test_callback(): callbacklist = [] def callback(): callbacklist.append(1) with utils.obj_in_module(callbacks, 'callback', callback): callbacks._callback() assert tuple(callbacklist) == (1,) def test_yesnocancel(): def yesnocancel(question): return 1 question = openrlib.ffi.new('char []', b'What ?') with utils.obj_in_module(callbacks, 'yesnocancel', yesnocancel): res = callbacks._yesnocancel(question) assert res == 1 rpy2-3.2.6/rpy2/tests/rinterface/test_endr.py0000644000175000017500000000064613615570032022315 0ustar laurentlaurent00000000000000import pytest from rpy2 import rinterface from rpy2.rinterface import embedded @pytest.mark.skipif(embedded.rpy2_embeddedR_isinitialized, reason='This test should be run independently of other tests.') def test_endr(): rinterface.initr() embedded.endr(1) assert (embedded.rpy2_embeddedR_isinitialized & embedded.RPY_R_Status.ENDED.value) == embedded.RPY_R_Status.ENDED.value rpy2-3.2.6/rpy2/tests/rinterface/test_vectors.py0000644000175000017500000000714313576515767023075 0ustar laurentlaurent00000000000000import subprocess import pytest import sys import textwrap import rpy2.rinterface as ri ri.initr() def floatEqual(x, y, epsilon=0.00000001): return abs(x - y) < epsilon def test_int(): sexp = ri.IntSexpVector([1, ]) isInteger = ri.globalenv.find("is.integer") assert isInteger(sexp)[0] def test_float(): sexp = ri.IntSexpVector([1.0, ]) isNumeric = ri.globalenv.find("is.numeric") assert isNumeric(sexp)[0] def test_str(): sexp = ri.StrSexpVector(["a", ]) isStr = ri.globalenv.find("is.character") assert isStr(sexp)[0] def test_bool(): sexp = ri.BoolSexpVector([True, ]) isBool = ri.globalenv.find("is.logical") assert isBool(sexp)[0] def test_complex(): sexp = ri.ComplexSexpVector([1+2j, ]) is_complex = ri.globalenv.find("is.complex") assert is_complex(sexp)[0] def test_byte(): seq = (b'a', b'b') sexp = ri.ByteSexpVector(seq) is_raw = ri.globalenv.find("is.raw") assert is_raw(sexp)[0] def test_del(): v = ri.IntSexpVector(range(10)) with pytest.raises(AttributeError): v.__delitem__(3) def test_from_bool(): sexp = ri.vector([True, ], ri.RTYPES.LGLSXP) isLogical = ri.globalenv.find('is.logical') assert isLogical(sexp)[0] assert sexp[0] is True sexp = ri.vector(['a', ], ri.RTYPES.LGLSXP) isLogical = ri.globalenv.find('is.logical') assert isLogical(sexp)[0] assert sexp[0] def test_from_int(): sexp = ri.vector([1, ], ri.RTYPES.INTSXP) isInteger = ri.globalenv.find('is.integer') assert isInteger(sexp)[0] with pytest.raises(ValueError): ri.vector(['a', ], ri.RTYPES.INTSXP) def test_from_invalid_no_length(): s = (x for x in range(30)) with pytest.raises(TypeError): ri.vector(s, ri.RTYPES.INTSXP) def test_from_float(): sexp = ri.vector([1.0, ], ri.RTYPES.REALSXP) isNumeric = ri.globalenv.find("is.numeric") assert isNumeric(sexp)[0] def test_from_float_nan(): with pytest.raises(ValueError): ri.vector(["a", ], ri.RTYPES.REALSXP) def test_from_complex(): sexp = ri.vector([1.0 + 1.0j, ], ri.RTYPES.CPLXSXP) isComplex = ri.globalenv.find('is.complex') assert isComplex(sexp)[0] def test_from_string(): sexp = ri.vector(['abc', ], ri.RTYPES.STRSXP) isCharacter = ri.globalenv.find('is.character') assert isCharacter(sexp)[0] def test_from_list(): seq = (ri.FloatSexpVector([1.0]), ri.IntSexpVector([2, 3]), ri.StrSexpVector(['foo', 'bar'])) sexp = ri.ListSexpVector(seq) isList = ri.globalenv.find('is.list') assert isList(sexp)[0] assert len(sexp) == 3 def test_missing_R_Preserve_object_bug(): rgc = ri.baseenv['gc'] xx = range(100000) x = ri.IntSexpVector(xx) rgc() assert x[0] == 0 def test_invalid_rtype(): with pytest.raises(ValueError): ri.vector([1, ], -1) with pytest.raises(ValueError): ri.vector([1, ], 250) def test_invalid_not_vector_rtype(): with pytest.raises(ValueError): ri.vector([1, ], ri.RTYPES.ENVSXP) _instantiate_without_initr = textwrap.dedent( """ import rpy2.rinterface as rinterface try: tmp = rinterface.vector([1,2], rinterface.RTYPES.INTSXP) res = 'OK' except rinterface.embedded.RNotReadyError as re: res = 'Error: R not ready.' except Exception as e: res = 'Exception: %s' % str(e) print(res) """) def test_instantiate_without_initr(): output = subprocess.check_output( (sys.executable, '-c', _instantiate_without_initr.encode('ASCII'))) assert output.rstrip() == b'Error: R not ready.'.rstrip() rpy2-3.2.6/rpy2/tests/rinterface/test_vector_str.py0000644000175000017500000000417113576515767023600 0ustar laurentlaurent00000000000000import pytest import rpy2.rinterface as ri ri.initr() def test_init_from_seqr(): seq = ['foo', 'bar', 'baz'] v = ri.StrSexpVector(seq) assert len(v) == 3 for x, y in zip(seq, v): assert x == y def test_init_from_seq_invalid_item(): seq = ['foo', 0, 'baz'] with pytest.raises(Exception): ri.StrSexpVector(seq) def test_getitem(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) assert vec[1] == 'bar' with pytest.raises(TypeError): vec[(2, 3)] def test_setitem(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) vec[1] = 'boo' assert vec[1] == 'boo' with pytest.raises(TypeError): vec[(2, 3)] = 'boo' def test_getslice(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) vec_s = vec[0:2] assert len(vec_s) == 2 assert vec_s[0] == 'foo' assert vec_s[1] == 'bar' def test_getslice_negative(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) vec_s = vec[-2:-1] assert len(vec_s) == 1 assert vec_s[0] == 'bar' def test_setslice(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) vec[0:2] = ['boo', 'noo'] assert len(vec) == 3 assert vec[0] == 'boo' assert vec[1] == 'noo' def test_setslice_negative(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) vec[-2:-1] = ri.StrSexpVector(['boo', ]) assert len(vec) == 3 assert vec[1] == 'boo' def test_index(): vec = ri.StrSexpVector(['foo', 'bar', 'baz']) assert vec.index('bar') == 1 assert vec.index('baz') == 2 with pytest.raises(ValueError): vec.index(2) with pytest.raises(ValueError): vec.index('a') def test_non_asciil(): u_char = '\u21a7' b_char = b'\xe2\x86\xa7' assert(b_char == u_char.encode('utf-8')) sexp = ri.StrSexpVector((u'\u21a7', )) char = sexp[0] assert isinstance(char, str) # FIXME: the following line is failing on drone, but not locally # assert u'\u21a7'.encode('utf-8') == char.encode('utf-8') # because of this, the following line is used to pass the test # until I have more reports from users or manage to reproduce # myself what is happening on drone.io. rpy2-3.2.6/rpy2/tests/rinterface/test_symbol.py0000644000175000017500000000103613576515767022710 0ustar laurentlaurent00000000000000import pytest import rpy2.rinterface as rinterface rinterface.initr() def test_new_invalid(): x = 1 with pytest.raises(TypeError): rinterface.SexpSymbol(x) def test_new_missing(): with pytest.raises(TypeError): rinterface.SexpSymbol() def test_new_fromstring(): symbol = rinterface.SexpSymbol('pi') evalsymbol = rinterface.baseenv['eval'](symbol) assert evalsymbol.rid == rinterface.baseenv['pi'].rid def test_new_str(): symbol = rinterface.SexpSymbol('pi') assert 'pi' == str(symbol) rpy2-3.2.6/rpy2/tests/rinterface/test_na.py0000644000175000017500000000655713576515767022016 0ustar laurentlaurent00000000000000import pytest import math import rpy2.rinterface as ri ri.initr() def test_r_to_NAInteger(): na_int = ri.NA_Integer r_na_int = ri.evalr("NA_integer_")[0] assert r_na_int is na_int def test_NAInteger_repr(): na = ri.NA_Integer assert repr(na) == 'NA_integer_' def test_NAInteger_str(): na = ri.NA_Integer assert str(na) == 'NA_integer_' def test_NAInteger_to_r(): na_int = ri.NA_Integer assert ri.baseenv["is.na"](na_int)[0] def test_bool_NAInteger(): with pytest.raises(ValueError): bool(ri.NA_Integer) @pytest.mark.skip( reason="Python changed the behavior for int-inheriting objects.") def test_NAInteger_binaryfunc(): na_int = ri.NAInteger assert (na_int + 2) is na_int def test_NAInteger_in_vector(): na_int = ri.NA_Integer x = ri.IntSexpVector((1, na_int, 2)) assert x[1] is na_int assert x[0] == 1 assert x[2] == 2 def test_R_to_NALogical(): r_na_lgl = ri.evalr('NA')[0] assert r_na_lgl is ri.NA def test_NALogical_repr(): na = ri.NA_Logical assert repr(na) == 'NA' def test_NALogical_str(): na = ri.NA_Logical assert str(na) == 'NA' def test_bool_NALogical(): with pytest.raises(ValueError): bool(ri.NA) def test_NALogical_to_r(): na_lgl = ri.NA_Logical assert ri.baseenv["is.na"](na_lgl)[0] is True def test_NALogical_in_vector(): na_bool = ri.NA_Logical x = ri.BoolSexpVector((True, na_bool, False)) assert x[0] is True assert x[1] is ri.NA_Logical assert x[2] is False def test_R_to_NAReal(): r_na_real = ri.evalr('NA_real_')[0] assert math.isnan(r_na_real) def test_NAReal_to_r(): na_real = ri.NA_Real assert ri.baseenv["is.na"](na_real)[0] def test_bool_NAReal(): with pytest.raises(ValueError): bool(ri.NA_Real) def test_NAReal_binaryfunc(): na_real = ri.NA_Real assert math.isnan(na_real + 2.0) def test_NAReal_in_vector(): na_float = ri.NA_Real x = ri.FloatSexpVector((1.1, na_float, 2.2)) assert math.isnan(x[1]) assert x[0] == 1.1 assert x[2] == 2.2 def test_NAReal_repr(): na_float = ri.NA_Real assert repr(na_float) == 'NA_real_' def test_NAReal_str(): na_float = ri.NA_Real assert str(na_float) == 'NA_real_' def test_r_to_NACharacter(): na_character = ri.NA_Character r_na_character = ri.evalr("NA_character_") assert r_na_character.typeof == ri.RTYPES.STRSXP assert len(r_na_character) == 1 assert r_na_character.get_charsxp(0).rid == na_character.rid def test_NACharacter_repr(): na = ri.NA_Character assert repr(na) == 'NA_character_' def test_NACharacter_str(): na = ri.NA_Character assert str(na) == 'NA_character_' def test_NACharacter_to_r(): na_character = ri.NA_Character assert ri.baseenv["is.na"](ri.StrSexpVector((na_character, )))[0] def test_NACharacter_in_vector(): na_str = ri.NA_Character x = ri.StrSexpVector(("ab", na_str, "cd")) assert x[0] == 'ab' assert x.get_charsxp(1).rid == na_str.rid assert x[2] == 'cd' def test_R_to_NAComplex(): r_na_complex = ri.evalr('NA_complex_')[0] assert math.isnan(r_na_complex.real) assert math.isnan(r_na_complex.imag) def test_NAComplex_to_r(): na_complex = ri.NA_Complex assert ri.baseenv["is.na"](na_complex)[0] def test_bool_NAComplex(): with pytest.raises(ValueError): bool(ri.NA_Complex) rpy2-3.2.6/rpy2/tests/rinterface/test_vector_byte.py0000644000175000017500000000356313576515767023737 0ustar laurentlaurent00000000000000import array import pytest import rpy2.rinterface as ri ri.initr() def test_init_from_bytes_in_seq(): seq = (b'a', b'b', b'c') v = ri.ByteSexpVector(seq) assert len(v) == 3 for x, y in zip(seq, v): assert ord(x) == y def test_init_from_seq_of_bytes(): seq = (b'a', b'b', b'c') v = ri.ByteSexpVector(seq) assert len(v) == 3 for x, y in zip(seq, v): assert ord(x) == y def test_init_from_bytes(): seq = b'abc' v = ri.ByteSexpVector(seq) assert len(v) == 3 for x, y in zip(seq, v): assert x == y def test_init_from_seq_invalid_byte(): seq = (b'a', [], b'c') with pytest.raises(ValueError): ri.ByteSexpVector(seq) def test_from_memoryview(): a = array.array('b', b'abcdefg') mv = memoryview(a) vec = ri.ByteSexpVector.from_memoryview(mv) assert tuple(b'abcdefg') == tuple(vec) def test_getitem(): vec = ri.ByteSexpVector((b'a', b'b', b'c')) assert vec[1] == ord(b'b') def test_getitem_slice(): vec = ri.ByteSexpVector((b'a', b'b', b'c')) assert tuple(vec[:2]) == (ord(b'a'), ord(b'b')) def test_setitem(): vec = ri.ByteSexpVector((b'a', b'b', b'c')) vec[1] = b'z' assert vec[1] == ord(b'z') def test_setitem_int(): vec = ri.ByteSexpVector((b'a', b'b', b'c')) vec[1] = ord(b'z') assert vec[1] == ord(b'z') def test_setitem_int_invalid(): vec = ri.ByteSexpVector((b'a', b'b', b'c')) with pytest.raises(ValueError): vec[1] = 333 def test_setitem_slice(): values = (b'a', b'b', b'c') vec = ri.ByteSexpVector(values) vec[:2] = b'yz' assert tuple(vec) == tuple(b'yzc') def test_setitem_slice_invalid(): values = (b'a', b'b', b'c') vec = ri.ByteSexpVector(values) with pytest.raises(TypeError): vec['foo'] = (333, ord(b'z')) with pytest.raises(ValueError): vec[:2] = (333, ord(b'z')) rpy2-3.2.6/rpy2/robjects/0000755000175000017500000000000013615572523016304 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/robjects/packages_utils.py0000644000175000017500000001167513576515767021703 0ustar laurentlaurent00000000000000""" Utility module with functions related to R packages (having these in this utility module rather than in packages.py prevents circular imports). """ from rpy2 import rinterface from warnings import warn from collections import defaultdict _packages = rinterface.baseenv['.packages'] _libpaths = rinterface.baseenv['.libPaths'] _find_package = rinterface.baseenv['find.package'] def get_packagepath(package): """ return the path to an R package installed """ res = _find_package(rinterface.StrSexpVector((package, ))) return res[0] # Functions to translate R symbols to Python symbols. # The functions are in this module in order to facilitate # their access from other modules (without circular dependencies). # It not necessarily the absolute best place to have the functions though. def default_symbol_r2python(rname): """Replace each dot (.) with an underscore (_).""" return rname.replace('.', '_') def default_symbol_resolve(symbol_mapping): """Resolve any conflict in a symbol mapping. The argument `symbol_mapping` maps candidate new symbol names (e.g., the names of Python attributes in the namespace returned by :func:`importr`) to a sequence of original symbol names (e.g., the names of objects in an R package). The purpose of this function is to resolved conflicts, that is situations where there is more than one original symbol name associated with a new symbol name. :param symbol_mapping: a :class:`dict` or dict-like object. :return: A 2-tuple with conflicts (a :class:`dict` mapping the new symbol to a sequence of matching symbols) and resolutions (a :class:`dict` mapping new). """ # dict to store the Python symbol -> R symbols mapping causing problems. conflicts = dict() resolutions = dict() for py_symbol, r_symbols in symbol_mapping.items(): n_r_symbols = len(r_symbols) if n_r_symbols == 1: continue elif n_r_symbols == 2: # more than one R symbol associated with this Python symbol try: idx = r_symbols.index(py_symbol) # there is an R symbol identical to the proposed Python symbol; # we keep that pair mapped, and change the Python symbol for # the other R symbol(s) according to PEP 0008 for i, s in enumerate(r_symbols): if i == idx: resolutions[py_symbol] = [s, ] else: new_py_symbol = py_symbol + '_' resolutions[new_py_symbol] = [s, ] except ValueError: # I am unsure about what to do at this point: # add it as a conflict conflicts[py_symbol] = r_symbols else: # no automatic resolution if more than 2 conflicts[py_symbol] = r_symbols return conflicts, resolutions def _map_symbols(rnames, translation=dict(), symbol_r2python=default_symbol_r2python, symbol_resolve=default_symbol_resolve): """ :param names: an iterable of rnames :param translation: a mapping for R name->python name :param symbol_r2python: a function to translate an R symbol into a (presumably valid) Python symbol :param symbol_resolve: a function to check a prospective set of translation and resolve conflicts if needed """ symbol_mapping = defaultdict(list) for rname in rnames: if rname in translation: rpyname = translation[rname] else: rpyname = symbol_r2python(rname) symbol_mapping[rpyname].append(rname) conflicts, resolutions = symbol_resolve(symbol_mapping) return (symbol_mapping, conflicts, resolutions) def _fix_map_symbols(symbol_mapping, conflicts, on_conflict, msg_prefix, exception): """ :param symbol_mapping: as returned by `_map_symbols` :param conflicts: as returned by `_map_symbols` :param on_conflict: action to take if conflict :param msg_prefix: prefix for error message :param exception: exception to raise """ if len(conflicts) > 0: msg = msg_prefix msg += '\n- '.join(('%s -> %s' % (k, ', '.join(v)) for k, v in conflicts.items())) if on_conflict == 'fail': msg += ('\nTo turn this exception into a simple ' 'warning use the parameter ' '`on_conflict="warn"`') raise exception(msg) elif on_conflict == 'warn': for k, v in conflicts.items(): if k in v: symbol_mapping[k] = [k, ] else: del(symbol_mapping[k]) warn(msg) else: raise ValueError('Invalid value for parameter "on_conflict"') rpy2-3.2.6/rpy2/robjects/packages.py0000644000175000017500000004520113615570032020427 0ustar laurentlaurent00000000000000import os import warnings from types import ModuleType from warnings import warn import rpy2.rinterface as rinterface from . import conversion from rpy2.robjects.functions import (SignatureTranslatedFunction, docstring_property, DocumentedSTFunction) from rpy2.robjects import Environment from rpy2.robjects.packages_utils import ( default_symbol_r2python, default_symbol_resolve, _map_symbols, _fix_map_symbols ) import rpy2.robjects.help as rhelp _require = rinterface.baseenv['require'] _library = rinterface.baseenv['library'] _as_env = rinterface.baseenv['as.environment'] _package_has_namespace = rinterface.baseenv['packageHasNamespace'] _system_file = rinterface.baseenv['system.file'] _get_namespace = rinterface.baseenv['getNamespace'] _get_namespace_version = rinterface.baseenv['getNamespaceVersion'] _get_namespace_exports = rinterface.baseenv['getNamespaceExports'] _loaded_namespaces = rinterface.baseenv['loadedNamespaces'] _globalenv = rinterface.globalenv _new_env = rinterface.baseenv["new.env"] StrSexpVector = rinterface.StrSexpVector # Fetching symbols in the namespace "utils" assumes that "utils" is loaded # (currently the case by default in R). _data = rinterface.baseenv['::'](StrSexpVector(('utils', )), StrSexpVector(('data', ))) _reval = rinterface.baseenv['eval'] _options = rinterface.baseenv['options'] def no_warnings(func): """ Decorator to run R functions without warning. """ def run_withoutwarnings(*args, **kwargs): warn_i = _options().do_slot('names').index('warn') oldwarn = _options()[warn_i][0] _options(warn=-1) try: res = func(*args, **kwargs) except Exception as e: # restore the old warn setting before propagating # the exception up _options(warn=oldwarn) raise e _options(warn=oldwarn) return res return run_withoutwarnings @no_warnings def _eval_quiet(expr): return _reval(expr) # FIXME: should this be part of the API for rinterface ? # (may be it is already the case and there is code # duplicaton ?) def reval(string, envir=_globalenv): """ Evaluate a string as R code :param string: R code :type string: a :class:`str` :param envir: an environment in which the environment should take place (default: R's global environment) """ p = rinterface.parse(string) res = _reval(p, envir=envir) return res def quiet_require(name, lib_loc=None): """ Load an R package /quietly/ (suppressing messages to the console). """ if lib_loc is None: lib_loc = "NULL" else: lib_loc = "\"%s\"" % (lib_loc.replace('"', '\\"')) expr_txt = ("suppressPackageStartupMessages(" "base::require(%s, lib.loc=%s))" % (name, lib_loc)) expr = rinterface.parse(expr_txt) ok = _eval_quiet(expr) return ok class PackageData(object): """ Datasets in an R package. In R datasets can be distributed with a package. Datasets can be: - serialized R objects - R code (that produces the dataset) For a given R packages, datasets are stored separately from the rest of the code and are evaluated/loaded lazily. The lazy aspect has been conserved and the dataset are only loaded or generated when called through the method 'fetch()'. """ _packagename = None _lib_loc = None _datasets = None def __init__(self, packagename, lib_loc=rinterface.NULL): self._packagename = packagename self._lib_loc def _init_setlist(self): _datasets = dict() # 2D array of information about datatsets tmp_m = _data(**{'package': StrSexpVector((self._packagename, )), 'lib.loc': self._lib_loc})[2] nrows, ncols = tmp_m.do_slot('dim') c_i = 2 for r_i in range(nrows): _datasets[tmp_m[r_i + c_i * nrows]] = None # FIXME: check if instance methods are overriden self._datasets = _datasets def names(self): """ Names of the datasets""" if self._datasets is None: self._init_setlist() return self._datasets.keys() def fetch(self, name): """ Fetch the dataset (loads it or evaluates the R associated with it. In R, datasets are loaded into the global environment by default but this function returns an environment that contains the dataset(s). """ if self._datasets is None: self._init_setlist() if name not in self._datasets: raise KeyError('Data set "%s" cannot be found' % name) env = _new_env() _data(StrSexpVector((name, )), **{'package': StrSexpVector((self._packagename, )), 'lib.loc': self._lib_loc, 'envir': env}) return Environment(env) class Package(ModuleType): """ Models an R package (and can do so from an arbitrary environment - with the caution that locked environments should mostly be considered). """ _env = None __rname__ = None _translation = None _rpy2r = None __fill_rpy2r__ = None __update_dict__ = None _exported_names = None _symbol_r2python = None __version__ = None __rdata__ = None def __init__(self, env, name, translation={}, exported_names=None, on_conflict='fail', version=None, symbol_r2python=default_symbol_r2python, symbol_resolve=default_symbol_resolve): """ Create a Python module-like object from an R environment, using the specified translation if defined. - env: R environment - name: package name - translation: `dict` with R names as keys and corresponding Python names as values - exported_names: `set` of names/symbols to expose to instance users - on_conflict: 'fail' or 'warn' (default: 'fail') - version: version string for the package - symbol_r2python: function to convert R symbols into Python symbols. The default translate `.` into `_`. - symbol_resolve: function to check the Python symbols obtained from `symbol_r2python`. """ super(Package, self).__init__(name) self._env = env self.__rname__ = name self._translation = translation mynames = tuple(self.__dict__) self._rpy2r = {} if exported_names is None: exported_names = set(self._env.keys()) self._exported_names = exported_names self._symbol_r2python = symbol_r2python self._symbol_resolve = symbol_resolve self.__fill_rpy2r__(on_conflict=on_conflict) self._exported_names = self._exported_names.difference(mynames) self.__version__ = version def __update_dict__(self, on_conflict='fail'): """ Update the __dict__ according to what is in the R environment """ for elt in self._rpy2r: del(self.__dict__[elt]) self._rpy2r.clear() self.__fill_rpy2r__(on_conflict=on_conflict) def __fill_rpy2r__(self, on_conflict='fail'): """ Fill the attribute _rpy2r. - on_conflict: 'fail' or 'warn' (default: 'fail') """ assert(on_conflict in ('fail', 'warn')) name = self.__rname__ (symbol_mapping, conflicts, resolutions) = _map_symbols( self._env, translation=self._translation, symbol_r2python=self._symbol_r2python, symbol_resolve=self._symbol_resolve ) msg_prefix = ('Conflict when converting R symbols' ' in the package "%s"' ' to Python symbols: \n-' % self.__rname__) exception = LibraryError _fix_map_symbols(symbol_mapping, conflicts, on_conflict, msg_prefix, exception) symbol_mapping.update(resolutions) reserved_pynames = set(dir(self)) for rpyname, rnames in symbol_mapping.items(): # last paranoid check if len(rnames) > 1: raise ValueError( 'Only one R name should be associated with %s ' '(and we have %s)' % (rpyname, str(rnames)) ) rname = rnames[0] if rpyname in reserved_pynames: raise LibraryError('The symbol ' + rname + ' in the package "' + name + '"' + ' is conflicting with' + ' a Python object attribute') self._rpy2r[rpyname] = rname if (rpyname != rname) and (rname in self._exported_names): self._exported_names.remove(rname) self._exported_names.add(rpyname) try: riobj = self._env[rname] except rinterface.RRuntimeError as rre: warn(str(rre)) rpyobj = conversion.rpy2py(riobj) if hasattr(rpyobj, '__rname__'): rpyobj.__rname__ = rname # TODO: shouldn't the original R name be also in the __dict__ ? self.__dict__[rpyname] = rpyobj def __repr__(self): s = super(Package, self).__repr__() return 'rpy2.robjects.packages.Package as a %s' % s # alias STF = SignatureTranslatedFunction class SignatureTranslatedPackage(Package): """ R package in which the R functions had their signatures 'translated' (that this the named parameters were made to to conform Python's rules for vaiable names).""" def __fill_rpy2r__(self, on_conflict='fail'): (super(SignatureTranslatedPackage, self) .__fill_rpy2r__(on_conflict=on_conflict)) for name, robj in self.__dict__.items(): if isinstance(robj, rinterface.Sexp) and \ robj.typeof == rinterface.RTYPES.CLOSXP: self.__dict__[name] = STF( self.__dict__[name], on_conflict=on_conflict, symbol_r2python=self._symbol_r2python, symbol_resolve=self._symbol_resolve ) # alias STP = SignatureTranslatedPackage class SignatureTranslatedAnonymousPackage(SignatureTranslatedPackage): def __init__(self, string, name): env = Environment() reval(string, env) super(SignatureTranslatedAnonymousPackage, self).__init__(env, name) # alias STAP = SignatureTranslatedAnonymousPackage class InstalledSTPackage(SignatureTranslatedPackage): @docstring_property(__doc__) def __doc__(self): doc = list(['Python representation of an R package.']) if not self.__rname__: doc.append('') else: try: doc.append(rhelp.docstring(self.__rname__, self.__rname__ + '-package', sections=['description'])) except rhelp.HelpNotFoundError: doc.append('[R help was not found]') return os.linesep.join(doc) def __fill_rpy2r__(self, on_conflict='fail'): (super(SignatureTranslatedPackage, self) .__fill_rpy2r__(on_conflict=on_conflict)) for name, robj in self.__dict__.items(): if isinstance(robj, rinterface.Sexp) and \ robj.typeof == rinterface.RTYPES.CLOSXP: self.__dict__[name] = DocumentedSTFunction( self.__dict__[name], packagename=self.__rname__ ) class InstalledPackage(Package): @docstring_property(__doc__) def __doc__(self): doc = list(['Python representation of an R package.', 'R arguments:', '']) if not self.__rname__: doc.append('') else: try: doc.append(rhelp.docstring(self.__rname__, self.__rname__ + '-package', sections=['description'])) except rhelp.HelpNotFoundError: doc.append('[R help was not found]') return os.linesep.join(doc) class WeakPackage(Package): """ 'Weak' R package, with which looking for symbols results in a warning (and a None returned) whenever the desired symbol is not found (rather than a traditional `AttributeError`). """ def __getattr__(self, name): res = self.__dict__.get(name) if res is None: warnings.warn( "The symbol '%s' is not in this R namespace/package." % name ) return res class LibraryError(ImportError): """ Error occuring when importing an R library """ pass class InstalledPackages(object): """ R packages installed. """ def __init__(self, lib_loc=None): libraryiqr = _library(**{'lib.loc': lib_loc}) lib_results_i = libraryiqr.do_slot('names').index('results') self.lib_results = libraryiqr[lib_results_i] self.nrows, self.ncols = self.lib_results.do_slot('dim') self.colnames = self.lib_results.do_slot('dimnames')[1] # column names self.lib_packname_i = self.colnames.index('Package') def isinstalled(self, packagename): if not isinstance(packagename, rinterface.StrSexpVector): rinterface.StrSexpVector((packagename, )) else: if len(packagename) > 1: raise ValueError("Only specify one package name at a time.") nrows = self.nrows lib_results, lib_packname_i = self.lib_results, self.lib_packname_i for i in range(0+lib_packname_i*nrows, nrows*(lib_packname_i+1), 1): if lib_results[i] == packagename: return True return False def __iter__(self): """ Iterate through rows, yield tuples at each iteration """ lib_results = self.lib_results nrows, ncols = self.nrows, self.ncols colrg = range(0, ncols) for row_i in range(nrows): yield tuple(lib_results[x*nrows+row_i] for x in colrg) def isinstalled(name, lib_loc=None): """ Find whether an R package is installed :param name: name of an R package :param lib_loc: specific location for the R library (default: None) :rtype: a :class:`bool` """ instapack = InstalledPackages(lib_loc) return instapack.isinstalled(name) def importr(name, lib_loc=None, robject_translations={}, signature_translation=True, suppress_messages=True, on_conflict='fail', symbol_r2python=default_symbol_r2python, symbol_resolve=default_symbol_resolve, data=True): """ Import an R package. Arguments: - name: name of the R package - lib_loc: specific location for the R library (default: None) - robject_translations: dict (default: {}) - signature_translation: (True or False) - suppress_message: Suppress messages R usually writes on the console (defaut: True) - on_conflict: 'fail' or 'warn' (default: 'fail') - symbol_r2python: function to translate R symbols into Python symbols - symbol_resolve: function to check the Python symbol obtained from `symbol_r2python`. - data: embed a PackageData objects under the attribute name __rdata__ (default: True) Return: - an instance of class SignatureTranslatedPackage, or of class Package """ rname = rinterface.StrSexpVector((name, )) if suppress_messages: ok = quiet_require(name, lib_loc=lib_loc) else: ok = _require(rinterface.StrSexpVector(rname), **{'lib.loc': rinterface.StrSexpVector((lib_loc, ))})[0] if not ok: raise LibraryError("The R package %s could not be imported" % name) if _package_has_namespace(rname, _system_file(package=rname)): env = _get_namespace(rname) version = _get_namespace_version(rname)[0] exported_names = set(_get_namespace_exports(rname)) else: env = _as_env(rinterface.StrSexpVector(['package:'+name, ])) exported_names = None version = None if signature_translation: pack = InstalledSTPackage(env, name, translation=robject_translations, exported_names=exported_names, on_conflict=on_conflict, version=version, symbol_r2python=symbol_r2python, symbol_resolve=symbol_resolve) else: pack = InstalledPackage(env, name, translation=robject_translations, exported_names=exported_names, on_conflict=on_conflict, version=version, symbol_r2python=symbol_r2python, symbol_resolve=symbol_resolve) if data: if pack.__rdata__ is not None: warn('While importing the R package "%s", the rpy2 Package object ' 'is masking a translated R symbol "__rdata__" already present' % name) pack.__rdata__ = PackageData(name, lib_loc=lib_loc) return pack def data(package): """ Return the PackageData for the given package.""" return package.__rdata__ def wherefrom(symbol: str, startenv: rinterface.SexpEnvironment = rinterface.globalenv): """ For a given symbol, return the environment this symbol is first found in, starting from 'startenv'. """ env = startenv while True: if symbol in env: break env = env.enclos if env.rsame(rinterface.emptyenv): break return conversion.rpy2py(env) class ParsedCode(rinterface.ExprSexpVector): pass class SourceCode(str): _parsed = None def parse(self): if self._parsed is None: self._parsed = ParsedCode(rinterface.parse(self)) return self._parsed def as_namespace(self, name): """ Name for the namespace """ return SignatureTranslatedAnonymousPackage(self, name) rpy2-3.2.6/rpy2/robjects/numpy2ri.py0000644000175000017500000001470613615570032020444 0ustar laurentlaurent00000000000000import rpy2.robjects as ro import rpy2.robjects.conversion as conversion import rpy2.rinterface as rinterface from rpy2.rinterface import (Sexp, ListSexpVector, StrSexpVector, ByteSexpVector, RTYPES) import numpy # TODO: move this to rinterface. RINT_SIZE = 32 original_converter = None # The possible kind codes are listed at # http://numpy.scipy.org/array_interface.shtml _kinds = { # "t" -> not really supported by numpy 'b': ro.vectors.BoolVector, 'i': ro.vectors.IntVector, # "u" -> special-cased below 'f': ro.vectors.FloatVector, 'c': ro.vectors.ComplexVector, # "O" -> special-cased below 'S': ro.vectors.ByteVector, 'U': ro.vectors.StrVector, # "V" -> special-cased below # TODO: datetime64 ? # "datetime64": } _vectortypes = (RTYPES.LGLSXP, RTYPES.INTSXP, RTYPES.REALSXP, RTYPES.CPLXSXP, RTYPES.STRSXP) converter = conversion.Converter('original numpy conversion') py2rpy = converter.py2rpy rpy2py = converter.rpy2py def numpy_O_py2rpy(o): if all(isinstance(x, str) for x in o): res = StrSexpVector(o) elif all(isinstance(x, bytes) for x in o): res = ByteSexpVector(o) else: res = conversion.py2rpy(list(o)) return res def _numpyarray_to_r(a, func): # "F" means "use column-major order" vec = func(numpy.ravel(a, order='F')) # TODO: no dimnames ? # TODO: optimize what is below needed/possible ? # (other ways to create R arrays ?) dim = ro.vectors.IntVector(a.shape) res = rinterface.baseenv['array'](vec, dim=dim) return res def unsignednumpyint_to_rint(intarray): """Convert a numpy array of unsigned integers to an R array.""" if intarray.nbytes >= (RINT_SIZE / 4): raise ValueError( 'Cannot convert numpy array of unsigned integers ' 'with {RINT_SIZE} bits or more.'.format(RINT_SIZE=RINT_SIZE) ) else: res = _numpyarray_to_r(intarray, _kinds['i']) return res @py2rpy.register(numpy.ndarray) def numpy2rpy(o): """ Augmented conversion function, converting numpy arrays into rpy2.rinterface-level R structures. """ if not o.dtype.isnative: raise ValueError('Cannot pass numpy arrays with non-native ' 'byte orders at the moment.') # Most types map onto R arrays: if o.dtype.kind in _kinds: res = _numpyarray_to_r(o, _kinds[o.dtype.kind]) # R does not support unsigned types: elif o.dtype.kind == 'u': res = unsignednumpyint_to_rint(o) # Array-of-PyObject is treated like a Python list: elif o.dtype.kind == 'O': res = numpy_O_py2rpy(o) # Record arrays map onto R data frames: elif o.dtype.kind == 'V': if o.dtype.names is None: raise ValueError('Nothing can be done for this numpy array ' 'type "%s" at the moment.' % (o.dtype,)) df_args = [] for field_name in o.dtype.names: df_args.append((field_name, conversion.py2rpy(o[field_name]))) res = ro.baseenv["data.frame"].rcall(tuple(df_args), ro.globalenv) # It should be impossible to get here: else: raise ValueError('Unknown numpy array type "%s".' % str(o.dtype)) return res @py2rpy.register(numpy.integer) def npint_py2rpy(obj): return rinterface.IntSexpVector([obj, ]) @py2rpy.register(numpy.floating) def npfloat_py2rpy(obj): return rinterface.FloatSexpVector([obj, ]) @py2rpy.register(object) def nonnumpy2rpy(obj): # allow array-like objects to also function with this module. if not isinstance(obj, numpy.ndarray) and hasattr(obj, '__array__'): obj = obj.__array__() return ro.default_converter.py2rpy(obj) elif original_converter is None: # This means that the conversion module was not "activated". # For now, go with the default_converter. # TODO: the conversion system needs an overhaul badly. return ro.default_converter.py2rpy(obj) else: # The conversion module was "activated" return original_converter.py2rpy(obj) # TODO: delete ? # @py2ro.register(numpy.ndarray) # def numpy2ro(obj): # res = numpy2ri(obj) # return ro.vectors.rtypeof2rotype[res.typeof](res) @rpy2py.register(ListSexpVector) def rpy2py_list(obj): if 'data.frame' in obj.rclass: # R "factor" vectors will not convert well by default # (will become integers), so we build a temporary list o2 # with the factors as strings. o2 = list() # An added complication is that the conversion defined # in this module will make __getitem__ at the robjects # level return numpy arrays for column in rinterface.ListSexpVector(obj): if 'factor' in column.rclass: levels = tuple(column.do_slot("levels")) column = tuple(levels[x-1] for x in column) o2.append(column) names = obj.do_slot('names') if names == rinterface.NULL: res = numpy.rec.fromarrays(o2) else: res = numpy.rec.fromarrays(o2, names=tuple(names)) else: # not a data.frame, yet is it still possible to convert it res = ro.default_converter.rpy2py(obj) return res @rpy2py.register(Sexp) def rpy2py_sexp(obj): if (obj.typeof in _vectortypes) and (obj.typeof != RTYPES.VECSXP): res = numpy.array(obj) else: res = ro.default_converter.rpy2py(obj) return res def activate(): global original_converter # If module is already activated, there is nothing to do if original_converter is not None: return original_converter = conversion.converter new_converter = conversion.Converter('numpy conversion', template=original_converter) for k, v in py2rpy.registry.items(): if k is object: continue new_converter.py2rpy.register(k, v) for k, v in rpy2py.registry.items(): if k is object: continue new_converter.rpy2py.register(k, v) conversion.set_conversion(new_converter) def deactivate(): global original_converter # If module has never been activated or already deactivated, # there is nothing to do if original_converter is None: return conversion.set_conversion(original_converter) original_converter = None rpy2-3.2.6/rpy2/robjects/pandas2ri.py0000644000175000017500000002126113615572135020542 0ustar laurentlaurent00000000000000"""This module handles the conversion of data structures between R objects handled by rpy2 and pandas objects.""" import rpy2.robjects.conversion as conversion import rpy2.rinterface as rinterface from rpy2.rinterface import (IntSexpVector, ListSexpVector, Sexp, SexpVector, StrSexpVector) from pandas.core.frame import DataFrame as PandasDataFrame from pandas.core.series import Series as PandasSeries from pandas.core.index import Index as PandasIndex from pandas.core.dtypes.api import is_datetime64_any_dtype import pandas import numpy import warnings from collections import OrderedDict from rpy2.robjects.vectors import (BoolVector, DataFrame, FactorVector, FloatSexpVector, StrVector, IntVector, POSIXct) # pandas is requiring numpy. We add the numpy conversion will be # activate in the function activate() below. import rpy2.robjects.numpy2ri as numpy2ri if not pandas.__version__.startswith('0.'): warnings.warn('pandas >= 1.0 is not supported.') original_converter = None ISOdatetime = rinterface.baseenv['ISOdatetime'] as_vector = rinterface.baseenv['as.vector'] converter = conversion.Converter('original pandas conversion') py2rpy = converter.py2rpy rpy2py = converter.rpy2py # numpy types for Pandas columns that require (even more) special handling dt_O_type = numpy.dtype('O') # pandas types for series of integer (optional missing) values. integer_array_types = ('Int8', 'Int16', 'Int32', 'Int64', 'UInt8', 'UInt16', 'UInt32', 'UInt64') @py2rpy.register(PandasDataFrame) def py2rpy_pandasdataframe(obj): od = OrderedDict() for name, values in obj.iteritems(): try: od[name] = conversion.py2rpy(values) except Exception as e: warnings.warn('Error while trying to convert ' 'the column "%s". Fall back to string conversion. ' 'The error is: %s' % (name, str(e))) od[name] = StrVector(values) return DataFrame(od) @py2rpy.register(PandasIndex) def py2rpy_pandasindex(obj): if obj.dtype.kind == 'O': return StrVector(obj) else: # pandas2ri should definitely not have to know which paths remain to be # converted by numpy2ri # Answer: the thing is that pandas2ri builds on the conversion # rules defined by numpy2ri - deferring to numpy2ri is allowing # us to reuse that code. return numpy2ri.numpy2rpy(obj) def py2rpy_categoryseries(obj): for c in obj.cat.categories: if not isinstance(c, str): raise ValueError('Converting pandas "Category" series to ' 'R factor is only possible when categories ' 'are strings.') res = IntSexpVector(list(rinterface.NA_Integer if x == -1 else x+1 for x in obj.cat.codes)) res.do_slot_assign('levels', StrSexpVector(obj.cat.categories)) if obj.cat.ordered: res.rclass = StrSexpVector(('ordered', 'factor')) else: res.rclass = StrSexpVector(('factor',)) return res @py2rpy.register(PandasSeries) def py2rpy_pandasseries(obj): if obj.dtype.name == 'O': warnings.warn('Element "%s" is of dtype "O" and converted ' 'to R vector of strings.' % obj.name) res = StrVector(obj) elif obj.dtype.name == 'category': res = py2rpy_categoryseries(obj) res = FactorVector(res) elif is_datetime64_any_dtype(obj.dtype): # time series tzname = obj.dt.tz.zone if obj.dt.tz else '' d = [IntVector([x.year for x in obj]), IntVector([x.month for x in obj]), IntVector([x.day for x in obj]), IntVector([x.hour for x in obj]), IntVector([x.minute for x in obj]), FloatSexpVector([x.second + x.microsecond * 1e-6 for x in obj])] res = ISOdatetime(*d, tz=StrSexpVector([tzname])) # TODO: can the POSIXct be created from the POSIXct constructor ? # (is ' 0 else -1 for x in numpy.array(obj)] res = pandas.Categorical.from_codes( codes, categories=list(obj.do_slot('levels')), ordered='ordered' in obj.rclass ) else: res = numpy2ri.rpy2py(obj) return res @rpy2py.register(FloatSexpVector) def rpy2py_floatvector(obj): if POSIXct.isrinstance(obj): return rpy2py(POSIXct(obj)) else: return numpy2ri.rpy2py(obj) @rpy2py.register(POSIXct) def rpy2py_posixct(obj): return pandas.to_datetime(tuple(obj.iter_localized_datetime()), errors='coerce') @rpy2py.register(ListSexpVector) def rpy2py_listvector(obj): if 'data.frame' in obj.rclass: res = rpy2py(DataFrame(obj)) else: res = numpy2ri.rpy2py(obj) return res @rpy2py.register(DataFrame) def rpy2py_dataframe(obj): items = OrderedDict((k, rpy2py(v) if isinstance(v, Sexp) else v) for k, v in obj.items()) res = PandasDataFrame.from_dict(items) res.index = obj.rownames return res def activate(): global original_converter # If module is already activated, there is nothing to do. if original_converter is not None: return original_converter = conversion.Converter( 'snapshot before pandas conversion', template=conversion.converter) numpy2ri.activate() new_converter = conversion.Converter('snapshot before pandas conversion', template=conversion.converter) numpy2ri.deactivate() for k, v in py2rpy.registry.items(): if k is object: continue new_converter.py2rpy.register(k, v) for k, v in rpy2py.registry.items(): if k is object: continue new_converter.rpy2py.register(k, v) conversion.set_conversion(new_converter) def deactivate(): global original_converter # If module has never been activated or already deactivated, # there is nothing to do if original_converter is None: return conversion.set_conversion(original_converter) original_converter = None rpy2-3.2.6/rpy2/robjects/functions.py0000644000175000017500000002005413576515767020704 0ustar laurentlaurent00000000000000import os import re from collections import OrderedDict from rpy2.robjects.robject import RObjectMixin import rpy2.rinterface as rinterface from rpy2.robjects import help from . import conversion from rpy2.robjects.packages_utils import (default_symbol_r2python, default_symbol_resolve, _map_symbols, _fix_map_symbols) baseenv_ri = rinterface.baseenv # Needed to avoid circular imports. _reval = rinterface.baseenv['eval'] __formals = baseenv_ri.find('formals') __args = baseenv_ri.find('args') __is_null = baseenv_ri.find('is.null') def _formals_fixed(func): tmp = __args(func) if __is_null(tmp)[0]: return rinterface.NULL else: return __formals(tmp) # docstring_property and DocstringProperty # from Bradley Froehle # https://gist.github.com/bfroehle/4041015 def docstring_property(class_doc): def wrapper(fget): return DocstringProperty(class_doc, fget) return wrapper class DocstringProperty(object): def __init__(self, class_doc, fget): self.fget = fget self.class_doc = class_doc def __get__(self, obj, objtype=None): if obj is None: return self.class_doc else: return self.fget(obj) def __set__(self, obj, value): raise AttributeError("Cannot set the attribute") def __delete__(self, obj): raise AttributeError("Cannot delete the attribute") def _repr_argval(obj): """ Helper functions to display an R object in the docstring. This a hack and will be hopefully replaced the extraction of information from the R help system.""" try: size = len(obj) if size == 1: if obj[0].rid == rinterface.MissingArg.rid: # no default value s = None elif obj[0].rid == rinterface.NULL.rid: s = 'rinterface.NULL' else: s = str(obj[0][0]) elif size > 1: s = '(%s, ...)' % str(obj[0][0]) else: s = str(obj) except Exception: s = str(obj) return s class Function(RObjectMixin, rinterface.SexpClosure): """ Python representation of an R function. """ __local = baseenv_ri.find('local') __call = baseenv_ri.find('call') __assymbol = baseenv_ri.find('as.symbol') __newenv = baseenv_ri.find('new.env') _local_env = None def __init__(self, *args, **kwargs): super(Function, self).__init__(*args, **kwargs) self._local_env = self.__newenv( hash=rinterface.BoolSexpVector((True, )) ) @docstring_property(__doc__) def __doc__(self): fm = _formals_fixed(self) doc = list(['Python representation of an R function.', 'R arguments:', '']) if fm is rinterface.NULL: doc.append('') for key, val in zip(fm.do_slot('names'), fm): if key == '...': val = 'R ellipsis (any number of parameters)' doc.append('%s: %s' % (key, _repr_argval(val))) return os.linesep.join(doc) def __call__(self, *args, **kwargs): new_args = [conversion.py2rpy(a) for a in args] new_kwargs = {} for k, v in kwargs.items(): # TODO: shouldn't this be handled by the conversion itself ? if isinstance(v, rinterface.Sexp): new_kwargs[k] = v else: new_kwargs[k] = conversion.py2rpy(v) res = super(Function, self).__call__(*new_args, **new_kwargs) res = conversion.rpy2py(res) return res def formals(self): """ Return the signature of the underlying R function (as the R function 'formals()' would). """ res = _formals_fixed(self) res = conversion.rpy2py(res) return res def rcall(self, *args): """ Wrapper around the parent method rpy2.rinterface.SexpClosure.rcall(). """ res = super(Function, self).rcall(*args) return res class SignatureTranslatedFunction(Function): """ Python representation of an R function, where the names in named argument are translated to valid argument names in Python. """ _prm_translate = None def __init__(self, sexp, init_prm_translate=None, on_conflict='warn', symbol_r2python=default_symbol_r2python, symbol_resolve=default_symbol_resolve): super(SignatureTranslatedFunction, self).__init__(sexp) if init_prm_translate is None: prm_translate = OrderedDict() else: assert isinstance(init_prm_translate, dict) prm_translate = init_prm_translate formals = self.formals() if formals.__sexp__._cdata != rinterface.NULL.__sexp__._cdata: (symbol_mapping, conflicts, resolutions) = _map_symbols( formals.names, translation=prm_translate, symbol_r2python=symbol_r2python, symbol_resolve=symbol_resolve) msg_prefix = ('Conflict when converting R symbols' ' in the function\'s signature:\n- ') exception = ValueError _fix_map_symbols(symbol_mapping, conflicts, on_conflict, msg_prefix, exception) symbol_mapping.update(resolutions) reserved_pynames = set(dir(self)) prm_translate.update((k, v[0]) for k, v in symbol_mapping.items()) self._prm_translate = prm_translate if hasattr(sexp, '__rname__'): self.__rname__ = sexp.__rname__ def __call__(self, *args, **kwargs): prm_translate = self._prm_translate for k in tuple(kwargs.keys()): r_k = prm_translate.get(k, None) if r_k is not None: v = kwargs.pop(k) kwargs[r_k] = v return (super(SignatureTranslatedFunction, self) .__call__(*args, **kwargs)) pattern_link = re.compile(r'\\link\{(.+?)\}') pattern_code = re.compile(r'\\code\{(.+?)\}') pattern_samp = re.compile(r'\\samp\{(.+?)\}') class DocumentedSTFunction(SignatureTranslatedFunction): def __init__(self, sexp, init_prm_translate=None, packagename=None): super(DocumentedSTFunction, self).__init__(sexp, init_prm_translate=init_prm_translate) self.__rpackagename__ = packagename @docstring_property(__doc__) def __doc__(self): doc = ['Python representation of an R function.'] description = help.docstring(self.__rpackagename__, self.__rname__, sections=['description']) doc.append(description) fm = _formals_fixed(self) names = fm.do_slot('names') doc.append(self.__rname__+'(') for key, val in self._prm_translate.items(): if key == '___': description = ('(was "..."). R ellipsis ' '(any number of parameters)') else: description = _repr_argval(fm[names.index(val)]) if description is None: doc.append(' %s,' % key) else: doc.append(' %s = %s,' % (key, description)) doc.extend((')', '')) package = help.Package(self.__rpackagename__) page = package.fetch(self.__rname__) for item in page.arguments(): description = item.value description = description.replace('\n', '') description, count = pattern_link.subn(r'\1', description) description, count = pattern_code.subn(r'`\1`', description) description, count = pattern_samp.subn(r'`\1`', description) doc.append(' '.join((item.name, ': ', description, ','))) doc.append('') return os.linesep.join(doc) rpy2-3.2.6/rpy2/robjects/lib/0000755000175000017500000000000013615572523017052 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/robjects/lib/dplyr.py0000644000175000017500000002224113613106240020542 0ustar laurentlaurent00000000000000from collections import namedtuple from rpy2 import robjects from rpy2.robjects.packages import (importr, WeakPackage) import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") dplyr = importr('dplyr', on_conflict="warn") lazyeval = importr('lazyeval', on_conflict="warn") dplyr = WeakPackage(dplyr._env, dplyr.__rname__, translation=dplyr._translation, exported_names=dplyr._exported_names, on_conflict="warn", version=dplyr.__version__, symbol_r2python=dplyr._symbol_r2python, symbol_resolve=dplyr._symbol_resolve) TARGET_VERSION = '0.8.3' if dplyr.__version__ != TARGET_VERSION: warnings.warn('This was designed againt dplyr version %s but you have %s' % (TARGET_VERSION, dplyr.__version__)) StringInEnv = namedtuple('StringInEnv', 'string env') def _fix_args_inenv(args, env): """Use R's lazyeval::as_lazy to evaluate each argument in a sequence as code in an environment.""" args_inenv = list() for v in args: if isinstance(v, StringInEnv): args_inenv.append(lazyeval.as_lazy(v.string, env=v.env)) else: args_inenv.append(lazyeval.as_lazy(v, env=env)) return args_inenv def _fix_kwargs_inenv(kwargs, env): """Use R's lazyeval::as_lazy to evaluate each value in a dict as code in an environment.""" kwargs_inenv = dict() for k, v in kwargs.items(): if isinstance(v, StringInEnv): kwargs_inenv[k] = lazyeval.as_lazy(v.string, env=v.env) else: kwargs_inenv[k] = lazyeval.as_lazy(v, env=env) return kwargs_inenv # TODO: _wrap and _pipe have become quite similar (identical ?). # Have only one of the two ? def _wrap(rfunc, cls, env=robjects.globalenv): def func(dataf, *args, **kwargs): args_inenv = _fix_args_inenv(args, env) kwargs_inenv = _fix_kwargs_inenv(kwargs, env) res = rfunc(dataf, *args_inenv, **kwargs_inenv) if cls is None: return type(dataf)(res) else: return cls(res) return func def _wrap2(rfunc, cls, env=robjects.globalenv): def func(dataf_a, dataf_b, *args, **kwargs): res = rfunc(dataf_a, dataf_b, *args, **kwargs) if cls is None: return type(dataf_a)(res) else: return cls(res) return func def _make_pipe(rfunc, cls, env=robjects.globalenv): """ :param rfunc: An R function. :param cls: The class to use wrap the result of `rfunc`. :param env: A R environment. :rtype: A function.""" def inner(obj, *args, **kwargs): args_inenv = _fix_args_inenv(args, env) kwargs_inenv = _fix_kwargs_inenv(kwargs, env) res = rfunc(obj, *args_inenv, **kwargs_inenv) return cls(res) return inner def _make_pipe2(rfunc, cls, env=robjects.globalenv): """ :param rfunc: An R function. :param cls: The class to use wrap the result of `rfunc`. :param env: A R environment. :rtype: A function.""" def inner(obj_a, obj_b, *args, **kwargs): res = rfunc(obj_a, obj_b, *args, **kwargs) return cls(res) return inner class DataFrame(robjects.DataFrame): """DataFrame object extending the object of the same name in `rpy2.robjects.vectors` with functionalities defined in the R package `dplyr`.""" def __rshift__(self, other): return other(self) def copy_to(self, destination, name, **kwargs): """ - destination: database - name: table name in the destination database """ res = dplyr.copy_to(destination, self, name=name) return type(self)(res) def collapse(self, *args, **kwargs): """ Call the function `collapse` in the R package `dplyr`. """ cls = type(self) return cls(dplyr.collapse(self, *args, **kwargs)) def collect(self, *args, **kwargs): """Call the function `collect` in the R package `dplyr`.""" cls = type(self) return cls(dplyr.collect(self, *args, **kwargs)) class GroupedDataFrame(DataFrame): """DataFrame grouped by one of several factors.""" pass DataFrame.arrange = _wrap(dplyr.arrange_, None) DataFrame.distinct = _wrap(dplyr.distinct_, None) DataFrame.mutate = _wrap(dplyr.mutate_, None) DataFrame.transmute = _wrap(dplyr.transmute_, None) DataFrame.filter = _wrap(dplyr.filter_, None) DataFrame.select = _wrap(dplyr.select_, None) DataFrame.group_by = _wrap(dplyr.group_by_, GroupedDataFrame) DataFrame.ungroup = _wrap(dplyr.ungroup, None) DataFrame.distinct = _wrap(dplyr.distinct_, None) DataFrame.inner_join = _wrap2(dplyr.inner_join, None) DataFrame.left_join = _wrap2(dplyr.left_join, None) DataFrame.right_join = _wrap2(dplyr.right_join, None) DataFrame.full_join = _wrap2(dplyr.full_join, None) DataFrame.semi_join = _wrap2(dplyr.semi_join, None) DataFrame.anti_join = _wrap2(dplyr.anti_join, None) DataFrame.union = _wrap2(dplyr.union, None) DataFrame.intersect = _wrap2(dplyr.intersect, None) DataFrame.setdiff = _wrap2(dplyr.setdiff, None) DataFrame.sample_n = _wrap(dplyr.sample_n, None) DataFrame.sample_frac = _wrap(dplyr.sample_frac, None) DataFrame.slice = _wrap(dplyr.slice_, None) DataFrame.count = _wrap(dplyr.count_, None) DataFrame.tally = _wrap(dplyr.tally, None) DataFrame.mutate_if = _wrap(dplyr.mutate_if, None) DataFrame.mutate_at = _wrap(dplyr.mutate_at, None) DataFrame.mutate_all = _wrap(dplyr.mutate_all, None) DataFrame.summarize_all = _wrap(dplyr.summarize_all, None) DataFrame.summarise_all = _wrap(dplyr.summarize_all, None) DataFrame.summarize_at = _wrap(dplyr.summarize_at, None) DataFrame.summarise_at = _wrap(dplyr.summarize_at, None) DataFrame.summarize_if = _wrap(dplyr.summarize_if, None) DataFrame.summarise_if = _wrap(dplyr.summarize_if, None) DataFrame.transmute_all = _wrap(dplyr.transmute_all, None) DataFrame.transmute_if = _wrap(dplyr.transmute_if, None) DataFrame.transmute_at = _wrap(dplyr.transmute_at, None) GroupedDataFrame.summarize = _wrap(dplyr.summarize_, DataFrame) GroupedDataFrame.summarise = GroupedDataFrame.summarize GroupedDataFrame.ungroup = _wrap(dplyr.ungroup, DataFrame) arrange = _make_pipe(dplyr.arrange_, DataFrame) count = _make_pipe(dplyr.count_, DataFrame) distinct = _make_pipe(dplyr.distinct_, DataFrame) mutate = _make_pipe(dplyr.mutate_, DataFrame) transmute = _make_pipe(dplyr.transmute_, DataFrame) filter = _make_pipe(dplyr.filter_, DataFrame) select = _make_pipe(dplyr.select_, DataFrame) group_by = _make_pipe(dplyr.group_by_, DataFrame) summarize = _make_pipe(dplyr.summarize_, DataFrame) summarise = summarize distinct = _make_pipe(dplyr.distinct_, DataFrame) inner_join = _make_pipe2(dplyr.inner_join, DataFrame) left_join = _make_pipe2(dplyr.left_join, DataFrame) right_join = _make_pipe2(dplyr.right_join, DataFrame) full_join = _make_pipe2(dplyr.full_join, DataFrame) semi_join = _make_pipe2(dplyr.semi_join, DataFrame) anti_join = _make_pipe2(dplyr.anti_join, DataFrame) union = _make_pipe2(dplyr.union, DataFrame) intersect = _make_pipe2(dplyr.intersect, DataFrame) setdiff = _make_pipe2(dplyr.setdiff, DataFrame) sample_n = _make_pipe(dplyr.sample_n, DataFrame) sample_frac = _make_pipe(dplyr.sample_frac, DataFrame) slice = _make_pipe(dplyr.slice_, DataFrame) tally = _make_pipe(dplyr.tally, DataFrame) mutate_if = _make_pipe(dplyr.mutate_if, DataFrame) mutate_at = _make_pipe(dplyr.mutate_at, DataFrame) mutate_all = _make_pipe(dplyr.mutate_all, DataFrame) summarize_all = _make_pipe(dplyr.summarize_all, DataFrame) summarise_all = _make_pipe(dplyr.summarise_all, DataFrame) summarize_at = _make_pipe(dplyr.summarize_at, DataFrame) summarise_at = _make_pipe(dplyr.summarize_at, DataFrame) summarize_if = _make_pipe(dplyr.summarize_if, DataFrame) summarise_if = _make_pipe(dplyr.summarize_if, DataFrame) transmute_all = _make_pipe(dplyr.transmute_all, DataFrame) transmute_if = _make_pipe(dplyr.transmute_if, DataFrame) transmute_at = _make_pipe(dplyr.transmute_at, DataFrame) # Functions for databases class DataSource(robjects.ListVector): """ Source of data tables (e.g., in a schema in a relational database). """ @property def tablenames(self): """ Call the R function dplyr::src_tbls() and return a vector of table names.""" return tuple(dplyr.src_tbls(self)) def get_table(self, name): """ "Get" table from a source (R dplyr's function `tbl`). """ return DataFrame(tbl(self, name)) src = dplyr.src src_tbls = dplyr.src_tbls src_local = dplyr.src_local src_df = dplyr.src_df def src_mysql(*args, **kwargs): res = dplyr.src_mysql(*args, **kwargs) return DataSource(res) def src_postgres(*args, **kwargs): res = dplyr.src_postgres(*args, **kwargs) return DataSource(res) def src_sqlite(*args, **kwargs): res = dplyr.src_sqlite(*args, **kwargs) return DataSource(res) def copy_to(*args, **kwargs): res = dplyr.copy_to(*args, **kwargs) return DataFrame(res) # Generic to create a data table tbl = dplyr.tbl # TODO: wrapper classes for the output of the following two function. explain = dplyr.explain show_query = dplyr.show_query rpy2-3.2.6/rpy2/robjects/lib/grdevices.py0000644000175000017500000000444413576515767021422 0ustar laurentlaurent00000000000000""" Mapping of the R library "grDevices" for graphical devices """ import io import os import tempfile from contextlib import contextmanager from rpy2.robjects.packages import importr, WeakPackage grdevices = importr('grDevices') grdevices = WeakPackage(grdevices._env, grdevices.__rname__, translation=grdevices._translation, exported_names=grdevices._exported_names, on_conflict="warn", version=grdevices.__version__, symbol_r2python=grdevices._symbol_r2python, symbol_resolve=grdevices._symbol_resolve) # non-interactive file devices png = grdevices.png jpeg = grdevices.jpeg bmp = grdevices.bmp tiff = grdevices.tiff svg = grdevices.svg postscript = grdevices.postscript pdf = grdevices.pdf cairo_pdf = grdevices.cairo_pdf # X11 = grdevices.X11 # OS X quartz = grdevices.quartz # devices dev_list = grdevices.dev_list dev_cur = grdevices.dev_cur dev_next = grdevices.dev_next dev_prev = grdevices.dev_prev dev_set = grdevices.dev_set dev_off = grdevices.dev_off @contextmanager def render_to_file(device, *device_args, **device_kwargs): """ Context manager that returns a R figures in a file object. :param device: an R "device" function. This function is expected to take a filename as its first argument. """ # TODO: better function signature to check that a file name is passed. current = dev_cur()[0] try: device(*device_args, **device_kwargs) yield None finally: if current != dev_cur()[0]: dev_off() @contextmanager def render_to_bytesio(device, *device_args, **device_kwargs): """ Context manager that returns a R figures in a :class:`io.BytesIO` object. :param device: an R "device" function. This function is expected to take a filename as its first argument. """ fn = tempfile.mktemp() b = io.BytesIO() current = dev_cur()[0] try: device(fn, *device_args, **device_kwargs) yield b finally: if current != dev_cur()[0]: dev_off() if os.path.exists(fn): with open(fn, 'rb') as fh: b.write(fh.read()) os.unlink(fn) rpy2-3.2.6/rpy2/robjects/lib/tidyr.py0000644000175000017500000000231013615570032020544 0ustar laurentlaurent00000000000000from rpy2.robjects.packages import (importr, WeakPackage) from rpy2.robjects.lib import dplyr import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") tidyr = importr('tidyr', on_conflict="warn") TARGET_VERSION = '0.8.3' if tidyr.__version__ != TARGET_VERSION: warnings.warn('This was designed againt tidyr version %s ' 'but you have %s' % (TARGET_VERSION, tidyr.__version__)) tidyr = WeakPackage(tidyr._env, tidyr.__rname__, translation=tidyr._translation, exported_names=tidyr._exported_names, on_conflict="warn", version=tidyr.__version__, symbol_r2python=tidyr._symbol_r2python, symbol_resolve=tidyr._symbol_resolve) def _wrap(rfunc): def func(dataf, *args, **kwargs): cls = type(dataf) res = rfunc(dataf, *args, **kwargs) return cls(res) return func class DataFrame(dplyr.DataFrame): gather = _wrap(tidyr.gather) spread = _wrap(tidyr.spread) DataFrame.summarize = dplyr._wrap(dplyr.summarize, None) DataFrame.summarise = DataFrame.summarize rpy2-3.2.6/rpy2/robjects/lib/dbplyr.py0000644000175000017500000000156513576515767020744 0ustar laurentlaurent00000000000000from rpy2.robjects.packages import (importr, WeakPackage) import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") dbplyr = importr('dbplyr', on_conflict="warn") dbplyr = WeakPackage(dbplyr._env, dbplyr.__rname__, translation=dbplyr._translation, exported_names=dbplyr._exported_names, on_conflict="warn", version=dbplyr.__version__, symbol_r2python=dbplyr._symbol_r2python, symbol_resolve=dbplyr._symbol_resolve) TARGET_VERSION = '1.4.2' if dbplyr.__version__ != TARGET_VERSION: warnings.warn( 'This was designed against dbplyr version %s ' 'but you have %s' % (TARGET_VERSION, dbplyr.__version__)) sql = dbplyr.sql rpy2-3.2.6/rpy2/robjects/lib/ggplot2.py0000644000175000017500000006337513613106226021007 0ustar laurentlaurent00000000000000""" Wrapper for the popular R library ggplot2. With rpy2, the most convenient general way to import packages is to use `importr()`, for example with ggplot2:: from rpy2.robjects.packages import importr ggplot2 = importr('ggplot2') This module is an supplementary layer in which an attempt at modelling the package as it was really developed as Python package is made. Behind the scene, `importr()` is used and can be accessed with: from robjects.robjects.lib import ggplot2 ggplot2.ggplot2 GGplot2 is designed using a prototype-based approach to Object-Oriented Programming, and this module is trying to define class-hierachies so the nature of a given instance can be identified more easily. The main families of classes are: - GGplot - Aes and AesString - Layer - Stat A downside of the approach is that the code in the module is 'hand-made'. In hindsight, this can be tedious to maintain and document but this is a good showcase of "manual" mapping of R code into Python classes. The codebase in R for ggplot2 has evolved since this was initially written, and many functions have signature-defined parameters (used to be ellipsis about everywhere). Metaprogramming will hopefully be added to shorten the Python code in the module, and provide a more dynamic mapping. """ import rpy2.robjects as robjects import rpy2.robjects.constants import rpy2.robjects.conversion as conversion from rpy2.robjects.packages import importr, WeakPackage import copy import warnings NULL = robjects.NULL rlang = importr('rlang', on_conflict='warn', robject_translations={'.env': '__env'}) lazyeval = importr('lazyeval', on_conflict='warn') base = importr('base', on_conflict='warn') ggplot2 = importr('ggplot2', on_conflict='warn') ggplot2 = WeakPackage(ggplot2._env, ggplot2.__rname__, translation=ggplot2._translation, exported_names=ggplot2._exported_names, on_conflict="warn", version=ggplot2.__version__, symbol_r2python=ggplot2._symbol_r2python, symbol_resolve=ggplot2._symbol_resolve) TARGET_VERSION = '3.2.1' if ggplot2.__version__ != TARGET_VERSION: warnings.warn('This was designed againt ggplot2 version %s but you ' 'have %s' % (TARGET_VERSION, ggplot2.__version__)) ggplot2_env = robjects.baseenv['as.environment']('package:ggplot2') StrVector = robjects.StrVector def as_symbol(x): return rlang.sym(x) class GGPlot(robjects.vectors.ListVector): """ A Grammar of Graphics Plot. GGPlot instances can be added to one an other in order to construct the final plot (the method `__add__()` is implemented). """ _constructor = ggplot2._env['ggplot'] _rprint = ggplot2._env['print.ggplot'] _add = ggplot2._env['%+%'] @classmethod def new(cls, data): """ Constructor for the class GGplot. """ data = conversion.py2rpy(data) res = cls(cls._constructor(data)) return res def plot(self, vp=rpy2.robjects.constants.NULL): self._rprint(self, vp=vp) def __add__(self, obj): res = self._add(self, obj) if 'gg' not in res.rclass: raise ValueError( "Added object did not give a ggplot result " "(get class '%s')." % res.rclass[0]) return self.__class__(res) def save(self, filename, **kwargs): """ Save the plot ( calls R's `ggplot2::ggsave()` ) """ ggplot2.ggsave(filename=filename, plot=self, **kwargs) ggplot = GGPlot.new class Aes(robjects.ListVector): """ Aesthetics mapping, using expressions rather than string (this is the most common form when using the package in R - it might be easier to use AesString when working in Python using rpy2 - see class AesString in this Python module). """ _constructor = ggplot2_env['aes'] @classmethod def new(cls, **kwargs): """Constructor for the class Aes.""" new_kwargs = copy.copy(kwargs) for k, v in kwargs.items(): new_kwargs[k] = rlang.parse_quo( v, env=robjects.baseenv['sys.frame']() ) res = cls(cls._constructor(**new_kwargs)) return res aes = Aes.new class AesString(robjects.ListVector): """ Aesthetics mapping, using strings rather than expressions (the later being most common form when using the package in R - see class Aes in this Python module). This associates dimensions in the data sets (columns in the DataFrame), possibly with a transformation applied on-the-fly (e.g., "log(value)", or "cost / benefits") to graphical "dimensions" in a chosen graphical representation (e.g., x-axis, color of points, size, etc...). Not all graphical representations have all dimensions. Refer to the documentation of ggplot2, online tutorials, or Hadley's book for more details. """ _constructor = ggplot2_env['aes_string'] @classmethod def new(cls, **kwargs): """Constructor for the class AesString.""" res = cls(cls._constructor(**kwargs)) return res aes_string = AesString.new class Layer(robjects.RObject): """ At this level, aesthetics mapping can (should ?) be specified (see Aes and AesString). """ _constructor = ggplot2_env['layer'] @classmethod def new(cls, *args, **kwargs): """ Constructor for the class Layer. """ for i, elt in enumerate(args): args[i] = conversion.py2ro(elt) for k in kwargs: kwargs[k] = conversion.py2ro(kwargs[k]) res = cls(cls.contructor)(*args, **kwargs) return res layer = Layer.new class GBaseObject(robjects.Environment): @classmethod def new(cls, *args, **kwargs): args_list = list(args) res = cls(cls._constructor(*args_list, **kwargs)) return res class Stat(GBaseObject): """ A "statistical" processing of the data in order to make a plot, or a plot element. This is an abstract class; material classes are called Stat* (e.g., StatAbline, StatBin, etc...). """ pass class StatBin(Stat): """ Bin data. """ _constructor = ggplot2_env['stat_bin'] stat_bin = StatBin.new class StatBin2D(Stat): """ 2D binning of data into squares/rectangles. """ _constructor = ggplot2_env['stat_bin_2d'] stat_bin2d = StatBin2D.new stat_bin_2d = StatBin2D.new class StatBinhex(Stat): """ 2D binning of data into hexagons. """ _constructor = ggplot2_env['stat_bin_hex'] stat_binhex = StatBinhex.new stat_bin_hex = StatBinhex.new class StatBoxplot(Stat): """ Components of box and whisker plot. """ _constructor = ggplot2_env['stat_boxplot'] stat_boxplot = StatBoxplot.new class StatContour(Stat): """ Contours of 3D data. """ _constructor = ggplot2_env['stat_contour'] stat_contour = StatContour.new class StatDensity(Stat): """ 1D density estimate """ _constructor = ggplot2_env['stat_density'] stat_density = StatDensity.new class StatDensity2D(Stat): """ 2D density estimate """ _constructor = ggplot2_env['stat_density_2d'] stat_density2d = StatDensity2D.new stat_density_2d = StatDensity2D.new class StatFunction(Stat): """ Superimpose a function """ _constructor = ggplot2_env['stat_function'] stat_function = StatFunction.new class StatIdentity(Stat): """ Identity function """ _constructor = ggplot2_env['stat_identity'] stat_identity = StatIdentity.new class StatQQ(Stat): """ Calculation for quantile-quantile plot. """ _constructor = ggplot2_env['stat_qq'] stat_qq = StatQQ.new class StatQuantile(Stat): """ Continuous quantiles """ _constructor = ggplot2_env['stat_quantile'] stat_quantile = StatQuantile.new class StatSmooth(Stat): """ Smoothing function """ _constructor = ggplot2_env['stat_smooth'] stat_smooth = StatSmooth.new class StatSpoke(Stat): """ Convert angle and radius to xend and yend """ _constructor = ggplot2_env['stat_spoke'] stat_spoke = StatSpoke.new class StatSum(Stat): """ Sum unique values. Useful when overplotting. """ _constructor = ggplot2_env['stat_sum'] stat_sum = StatSum.new class StatSummary(Stat): """ Summarize values for y at every unique value for x""" _constructor = ggplot2_env['stat_summary'] stat_summary = StatSummary.new class StatSummary2D(Stat): """ Summarize values for y at every unique value for x""" _constructor = ggplot2_env['stat_summary_2d'] stat_summary2d = StatSummary2D.new stat_summary_2d = StatSummary2D.new class StatUnique(Stat): """ Remove duplicates. """ _constructor = ggplot2_env['stat_unique'] stat_unique = StatUnique.new class Coord(GBaseObject): """ Coordinates """ pass class CoordFixed(Coord): """ Cartesian coordinates with fixed relationship (that is fixed ratio between units in axes). CoordEquel seems to be identical to this class.""" _constructor = ggplot2_env['coord_fixed'] coord_fixed = CoordFixed.new class CoordCartesian(Coord): """ Cartesian coordinates. """ _constructor = ggplot2_env['coord_cartesian'] coord_cartesian = CoordCartesian.new class CoordEqual(Coord): """ This class seems to be identical to CoordFixed. """ _constructor = ggplot2_env['coord_equal'] coord_equal = CoordEqual.new class CoordFlip(Coord): """ Flip horizontal and vertical coordinates. """ _constructor = ggplot2_env['coord_flip'] coord_flip = CoordFlip.new class CoordMap(Coord): """ Map projections. """ _constructor = ggplot2_env['coord_map'] coord_map = CoordMap.new class CoordPolar(Coord): """ Polar coordinates. """ _constructor = ggplot2_env['coord_polar'] coord_polar = CoordPolar.new class CoordTrans(Coord): """ Apply transformations (functions) to a cartesian coordinate system. """ _constructor = ggplot2_env['coord_trans'] coord_trans = CoordTrans.new class Facet(GBaseObject): """ Panels """ pass class FacetGrid(Facet): """ Panels in a grid. """ _constructor = ggplot2_env['facet_grid'] facet_grid = FacetGrid.new class FacetWrap(Facet): """ Sequence of panels in a 2D layout """ _constructor = ggplot2_env['facet_wrap'] facet_wrap = FacetWrap.new class Geom(GBaseObject): pass class GeomAbline(Geom): _constructor = ggplot2_env['geom_abline'] geom_abline = GeomAbline.new class GeomArea(Geom): _constructor = ggplot2_env['geom_area'] geom_area = GeomArea.new class GeomBar(Geom): _constructor = ggplot2_env['geom_bar'] geom_bar = GeomBar.new class GeomCol(Geom): _constructor = ggplot2_env['geom_col'] geom_col = GeomCol.new class GeomBin2D(Geom): _constructor = ggplot2_env['geom_bin2d'] geom_bin2d = GeomBin2D.new class GeomBlank(Geom): _constructor = ggplot2_env['geom_blank'] geom_blank = GeomBlank.new class GeomBoxplot(Geom): _constructor = ggplot2_env['geom_boxplot'] geom_boxplot = GeomBoxplot.new class GeomContour(Geom): _constructor = ggplot2_env['geom_contour'] geom_contour = GeomContour.new class GeomCrossBar(Geom): _constructor = ggplot2_env['geom_crossbar'] geom_crossbar = GeomCrossBar.new class GeomDensity(Geom): _constructor = ggplot2_env['geom_density'] geom_density = GeomDensity.new class GeomDensity2D(Geom): _constructor = ggplot2_env['geom_density_2d'] geom_density2d = GeomDensity2D.new geom_density_2d = GeomDensity2D.new class GeomDotplot(Geom): _constructor = ggplot2_env['geom_dotplot'] geom_dotplot = GeomDotplot.new class GeomErrorBar(Geom): _constructor = ggplot2_env['geom_errorbar'] geom_errorbar = GeomErrorBar.new class GeomErrorBarH(Geom): _constructor = ggplot2_env['geom_errorbarh'] geom_errorbarh = GeomErrorBarH.new class GeomFreqPoly(Geom): _constructor = ggplot2_env['geom_freqpoly'] geom_freqpoly = GeomFreqPoly.new class GeomHex(Geom): _constructor = ggplot2_env['geom_hex'] geom_hex = GeomHex.new class GeomHistogram(Geom): _constructor = ggplot2_env['geom_histogram'] geom_histogram = GeomHistogram.new class GeomHLine(Geom): _constructor = ggplot2_env['geom_hline'] geom_hline = GeomHLine.new class GeomJitter(Geom): _constructor = ggplot2_env['geom_jitter'] geom_jitter = GeomJitter.new class GeomLine(Geom): _constructor = ggplot2_env['geom_line'] geom_line = GeomLine.new class GeomLineRange(Geom): _constructor = ggplot2_env['geom_linerange'] geom_linerange = GeomLineRange.new class GeomPath(Geom): _constructor = ggplot2_env['geom_path'] geom_path = GeomPath.new class GeomPoint(Geom): _constructor = ggplot2_env['geom_point'] geom_point = GeomPoint.new class GeomPointRange(Geom): _constructor = ggplot2_env['geom_pointrange'] geom_pointrange = GeomPointRange.new class GeomPolygon(Geom): _constructor = ggplot2_env['geom_polygon'] geom_polygon = GeomPolygon.new class GeomQuantile(Geom): _constructor = ggplot2_env['geom_quantile'] geom_quantile = GeomQuantile.new class GeomRaster(Geom): _constructor = ggplot2_env['geom_raster'] geom_raster = GeomRaster.new class GeomRect(Geom): _constructor = ggplot2_env['geom_rect'] geom_rect = GeomRect.new class GeomRibbon(Geom): _constructor = ggplot2_env['geom_ribbon'] geom_ribbon = GeomRibbon.new class GeomRug(Geom): _constructor = ggplot2_env['geom_rug'] geom_rug = GeomRug.new class GeomSegment(Geom): _constructor = ggplot2_env['geom_segment'] geom_segment = GeomSegment.new class GeomSmooth(Geom): _constructor = ggplot2_env['geom_smooth'] geom_smooth = GeomSmooth.new class GeomSpoke(Geom): """ Convert angle and radius to xend and yend """ _constructor = ggplot2_env['geom_spoke'] geom_spoke = GeomSpoke.new class GeomStep(Geom): _constructor = ggplot2_env['geom_step'] geom_step = GeomStep.new class GeomText(Geom): _constructor = ggplot2_env['geom_text'] geom_text = GeomText.new class GeomTile(Geom): _constructor = ggplot2_env['geom_tile'] geom_tile = GeomTile.new class GeomVLine(Geom): _constructor = ggplot2_env['geom_vline'] geom_vline = GeomVLine.new class Position(GBaseObject): pass class PositionDodge(Position): _constructor = ggplot2_env['position_dodge'] position_dodge = PositionDodge.new class PositionFill(Position): _constructor = ggplot2_env['position_fill'] position_fill = PositionFill.new class PositionJitter(Position): _constructor = ggplot2_env['position_jitter'] position_jitter = PositionJitter.new class PositionStack(Position): _constructor = ggplot2_env['position_stack'] position_stack = PositionStack.new class Scale(GBaseObject): pass class ScaleAlpha(Scale): _constructor = ggplot2_env['scale_alpha'] scale_alpha = ScaleAlpha.new class ScaleColour(Scale): pass class ScaleDiscrete(Scale): pass class ScaleLinetype(Scale): _constructor = ggplot2_env['scale_linetype'] scale_linetype = ScaleLinetype.new class ScaleShape(Scale): _constructor = ggplot2_env['scale_shape'] scale_shape = ScaleShape.new class ScaleRadius(Scale): _constructor = ggplot2_env['scale_radius'] scale_radius = ScaleRadius.new class ScaleSize(Scale): _constructor = ggplot2_env['scale_size'] scale_size = ScaleSize.new class ScaleSizeArea(Scale): _constructor = ggplot2_env['scale_size_area'] scale_size_area = ScaleSizeArea.new class ScaleShapeDiscrete(Scale): _constructor = ggplot2_env['scale_shape_discrete'] scale_shape_discrete = ScaleShapeDiscrete.new class ScaleFill(Scale): pass class ScaleX(Scale): pass class ScaleY(Scale): pass # class Limits(Scale): # _constructor = ggplot2_env['limits'] # limits = Limits.new class XLim(Scale): _constructor = ggplot2_env['xlim'] xlim = XLim.new class YLim(Scale): _constructor = ggplot2_env['ylim'] ylim = YLim.new class ScaleXContinuous(ScaleX): _constructor = ggplot2_env['scale_x_continuous'] scale_x_continuous = ScaleXContinuous.new class ScaleYContinuous(ScaleY): _constructor = ggplot2_env['scale_y_continuous'] scale_y_continuous = ScaleYContinuous.new class ScaleXDiscrete(ScaleX): _constructor = ggplot2_env['scale_x_discrete'] scale_x_discrete = ScaleXDiscrete.new class ScaleYDiscrete(ScaleY): _constructor = ggplot2_env['scale_y_discrete'] scale_y_discrete = ScaleYDiscrete.new class ScaleXDate(ScaleX): _constructor = ggplot2_env['scale_x_date'] scale_x_date = ScaleXDate.new class ScaleYDate(ScaleY): _constructor = ggplot2_env['scale_y_date'] scale_y_date = ScaleYDate.new class ScaleXDatetime(ScaleX): _constructor = ggplot2_env['scale_x_datetime'] scale_x_datetime = ScaleXDatetime.new class ScaleYDatetime(ScaleY): _constructor = ggplot2_env['scale_y_datetime'] scale_y_datetime = ScaleYDatetime.new class ScaleXLog10(ScaleX): _constructor = ggplot2_env['scale_x_log10'] scale_x_log10 = ScaleXLog10.new class ScaleYLog10(ScaleY): _constructor = ggplot2_env['scale_y_log10'] scale_y_log10 = ScaleYLog10.new class ScaleXReverse(ScaleX): _constructor = ggplot2_env['scale_x_reverse'] scale_x_reverse = ScaleXReverse.new class ScaleYReverse(ScaleY): _constructor = ggplot2_env['scale_y_reverse'] scale_y_reverse = ScaleYReverse.new class ScaleXSqrt(ScaleX): _constructor = ggplot2_env['scale_x_sqrt'] scale_x_sqrt = ScaleXSqrt.new class ScaleYSqrt(ScaleY): _constructor = ggplot2_env['scale_y_sqrt'] scale_y_sqrt = ScaleYSqrt.new class ScaleColourBrewer(ScaleColour): _constructor = ggplot2_env['scale_colour_brewer'] scale_colour_brewer = ScaleColourBrewer.new scale_color_brewer = scale_colour_brewer class ScaleColourContinuous(ScaleColour): _constructor = ggplot2_env['scale_colour_continuous'] scale_colour_continuous = ScaleColourContinuous.new scale_color_continuous = scale_colour_continuous class ScaleColourDiscrete(ScaleColour): _constructor = ggplot2_env['scale_colour_discrete'] scale_colour_discrete = ScaleColourDiscrete.new scale_color_discrete = scale_colour_discrete class ScaleColourGradient(ScaleColour): _constructor = ggplot2_env['scale_colour_gradient'] scale_colour_gradient = ScaleColourGradient.new scale_color_gradient = scale_colour_gradient class ScaleColourGradient2(ScaleColour): _constructor = ggplot2_env['scale_colour_gradient2'] scale_colour_gradient2 = ScaleColourGradient2.new scale_color_gradient2 = scale_colour_gradient2 class ScaleColourGradientN(ScaleColour): _constructor = ggplot2_env['scale_colour_gradientn'] scale_colour_gradientn = ScaleColourGradientN.new scale_color_gradientn = scale_colour_gradientn class ScaleColourGrey(ScaleColour): _constructor = ggplot2_env['scale_colour_grey'] scale_colour_grey = ScaleColourGrey.new scale_color_grey = scale_colour_grey class ScaleColourHue(ScaleColour): _constructor = ggplot2_env['scale_colour_hue'] scale_colour_hue = ScaleColourHue.new scale_color_hue = scale_colour_hue class ScaleColourIdentity(ScaleColour): _constructor = ggplot2_env['scale_colour_identity'] scale_colour_identity = ScaleColourIdentity.new scale_color_identity = scale_colour_identity class ScaleColourManual(ScaleColour): _constructor = ggplot2_env['scale_colour_manual'] scale_colour_manual = ScaleColourManual.new scale_color_manual = scale_colour_manual class ScaleFillBrewer(ScaleFill): _constructor = ggplot2_env['scale_fill_brewer'] scale_fill_brewer = ScaleFillBrewer.new class ScaleFillContinuous(ScaleFill): _constructor = ggplot2_env['scale_fill_continuous'] scale_fill_continuous = ScaleFillContinuous.new class ScaleFillDiscrete(ScaleFill): _constructor = ggplot2_env['scale_fill_discrete'] scale_fill_discrete = ScaleFillDiscrete.new class ScaleFillGradient(ScaleFill): _constructor = ggplot2_env['scale_fill_gradient'] scale_fill_gradient = ScaleFillGradient.new class ScaleFillGradient2(ScaleFill): _constructor = ggplot2_env['scale_fill_gradient2'] scale_fill_gradient2 = ScaleFillGradient2.new class ScaleFillGradientN(ScaleFill): _constructor = ggplot2_env['scale_fill_gradientn'] scale_fill_gradientn = ScaleFillGradientN.new class ScaleFillGrey(ScaleFill): _constructor = ggplot2_env['scale_fill_grey'] scale_fill_grey = ScaleFillGrey.new class ScaleFillHue(ScaleFill): _constructor = ggplot2_env['scale_fill_hue'] scale_fill_hue = ScaleFillHue.new class ScaleFillIdentity(ScaleFill): _constructor = ggplot2_env['scale_fill_identity'] scale_fill_identity = ScaleFillIdentity.new class ScaleFillManual(ScaleFill): _constructor = ggplot2_env['scale_fill_manual'] scale_fill_manual = ScaleFillManual.new class ScaleLinetypeContinuous(ScaleLinetype): _constructor = ggplot2_env['scale_linetype_continuous'] scale_linetype_continuous = ScaleLinetypeContinuous.new class ScaleLinetypeDiscrete(ScaleLinetype): _constructor = ggplot2_env['scale_linetype_discrete'] scale_linetype_discrete = ScaleLinetypeDiscrete.new class ScaleLinetypeManual(ScaleLinetype): _constructor = ggplot2_env['scale_linetype_manual'] scale_linetype_manual = ScaleLinetypeManual.new class ScaleShapeManual(ScaleShape): _constructor = ggplot2_env['scale_shape_manual'] scale_shape_manual = ScaleShapeManual.new guides = ggplot2.guides guide_colorbar = ggplot2.guide_colorbar guide_colourbar = ggplot2.guide_colourbar guide_legend = ggplot2.guide_legend class Options(robjects.ListVector): def __repr__(self): s = '' % (type(self), id(self)) return s class Element(Options): pass class ElementText(Element): _constructor = ggplot2.element_text @classmethod def new(cls, family='', face='plain', colour='black', size=10, hjust=0.5, vjust=0.5, angle=0, lineheight=1.1, color=NULL): res = cls(cls._constructor(family=family, face=face, colour=colour, size=size, hjust=hjust, vjust=vjust, angle=angle, lineheight=lineheight)) return res element_text = ElementText.new class ElementRect(Element): _constructor = ggplot2.element_rect @classmethod def new(cls, fill=NULL, colour=NULL, size=NULL, linetype=NULL, color=NULL): res = cls(cls._constructor(fill=fill, colour=colour, size=size, linetype=linetype, color=color)) return res element_rect = ElementRect.new class Labs(Options): _constructor = ggplot2.labs @classmethod def new(cls, **kwargs): res = cls(cls._constructor(**kwargs)) return res labs = Labs.new class Theme(Options): pass class ElementBlank(Theme): _constructor = ggplot2.element_blank @classmethod def new(cls): res = cls(cls._constructor()) return res element_blank = ElementBlank.new theme_get = ggplot2.theme_get class ThemeGrey(Theme): _constructor = ggplot2.theme_grey @classmethod def new(cls, base_size=12): res = cls(cls._constructor(base_size=base_size)) return res theme_grey = ThemeGrey.new class ThemeClassic(Theme): _constructor = ggplot2.theme_classic @classmethod def new(cls, base_size=12, base_family=''): res = cls(cls._constructor(base_size=base_size, base_family=base_family)) return res theme_classic = ThemeClassic.new class ThemeDark(Theme): _constructor = ggplot2.theme_dark @classmethod def new(cls, base_size=12, base_family=''): res = cls(cls._constructor(base_size=base_size, base_family=base_family)) return res theme_dark = ThemeDark.new class ThemeLight(Theme): _constructor = ggplot2.theme_light @classmethod def new(cls, base_size=12, base_family=''): res = cls(cls._constructor(base_size=base_size, base_family=base_family)) return res theme_light = ThemeLight.new class ThemeBW(Theme): _constructor = ggplot2.theme_bw @classmethod def new(cls, base_size=12): res = cls(cls._constructor(base_size=base_size)) return res theme_bw = ThemeBW.new class ThemeGray(Theme): _constructor = ggplot2.theme_gray @classmethod def new(cls, base_size=12): res = cls(cls._constructor(base_size=base_size)) return res theme_gray = ThemeGray.new theme_grey = theme_gray class ThemeMinimal(Theme): _constructor = ggplot2.theme_minimal @classmethod def new(cls, base_size=12, base_family=''): res = cls(cls._constructor(base_size=base_size, base_family=base_family)) return res theme_minimal = ThemeMinimal.new class ThemeLinedraw(Theme): _constructor = ggplot2.theme_linedraw @classmethod def new(cls, base_size=12, base_family=""): res = cls(cls._constructor(base_size=base_size, base_family=base_family)) return res theme_linedraw = ThemeLinedraw.new class ThemeVoid(Theme): _constructor = ggplot2.theme_void @classmethod def new(cls, base_size=12, base_family=''): res = cls(cls._constructor(base_size=base_size, base_family=base_family)) return res theme_void = ThemeVoid.new theme_set = ggplot2.theme_set theme_update = ggplot2.theme_update gplot = ggplot2.qplot map_data = ggplot2.map_data theme = ggplot2_env['theme'] ggtitle = ggplot2.ggtitle original_rpy2py = conversion.rpy2py def ggplot2_conversion(robj): pyobj = original_rpy2py(robj) try: rcls = pyobj.rclass except AttributeError: # conversion lead to something that is no # longer an R object return pyobj if (rcls is not None) and (rcls[0] == 'gg'): pyobj = GGPlot(pyobj) return pyobj conversion.rpy2py = ggplot2_conversion rpy2-3.2.6/rpy2/robjects/lib/__init__.py0000644000175000017500000000000013576515767021166 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/robjects/lib/grid.py0000644000175000017500000001547013576515767020375 0ustar laurentlaurent00000000000000""" Mapping of the R library "grid" for graphics. The R library provides a low-level coordinate system and graphic primitives to built visualizations. """ import rpy2.robjects as robjects import rpy2.robjects.conversion as conversion from rpy2.rlike.container import OrdDict from rpy2.robjects.packages import importr, WeakPackage NULL = robjects.NULL grid = importr('grid') grid = WeakPackage(grid._env, grid.__rname__, translation=grid._translation, exported_names=grid._exported_names, on_conflict="warn", version=grid.__version__, symbol_r2python=grid._symbol_r2python, symbol_resolve=grid._symbol_resolve) grid_env = robjects.baseenv['as.environment']('package:grid') layout = grid.grid_layout newpage = grid.grid_newpage grill = grid.grid_grill edit = grid.grid_edit get = grid.grid_get remove = grid.grid_remove add = grid.grid_add xaxis = grid.grid_xaxis yaxis = grid.grid_yaxis class Unit(robjects.RObject): """ Vector of unit values (as in R's grid package) """ _unit = grid_env['unit'] @classmethod def unit(cls, *args, **kwargs): """ Constructor (uses the R function grid::unit())""" res = cls._unit(*args, **kwargs) return res unit = Unit.unit class Gpar(robjects.RObject): """ Graphical parameters """ _gpar = grid_env['gpar'] _get_gpar = grid_env['get.gpar'] @classmethod def gpar(cls, *args, **kwargs): """ Constructor (uses the R function grid::gpar())""" res = cls._gpar(*args, **kwargs) return res def get(self, names=None): return self._get_gpar(names) gpar = Gpar.gpar class Grob(robjects.RObject): """ Graphical object """ _grob = grid_env['grob'] _draw = grid_env['grid.draw'] def __init__(self, *args, **kwargs): od = OrdDict() for item in args: od[None] = conversion.py2rpy(item) for k, v in kwargs.items(): od[k] = conversion.py2rpy(v) res = self._constructor.rcall(tuple(od.items()), robjects.globalenv) super().__init__(res.__sexp__) @classmethod def grob(cls, **kwargs): """ Constructor (uses the R function grid::grob())""" res = cls._grob(**kwargs) return res def draw(self, recording=True): """ Draw a graphical object (calling the R function grid::grid.raw())""" self._draw(self, recording=recording) grob = Grob.grob class Rect(Grob): _constructor = grid_env['rectGrob'] rect = Rect class Lines(Grob): _constructor = grid_env['linesGrob'] lines = Lines class Circle(Grob): _constructor = grid_env['circleGrob'] circle = Circle class Points(Grob): _constructor = grid_env['pointsGrob'] points = Points class Text(Grob): _constructor = grid_env['textGrob'] text = Text class GTree(Grob): """ gTree """ _gtree = grid_env['gTree'] _grobtree = grid_env['grobTree'] @classmethod def gtree(cls, **kwargs): """ Constructor (uses the R function grid::gTree())""" res = cls._gtree(**kwargs) return res @classmethod def grobtree(cls, **kwargs): """ Constructor (uses the R function grid::grobTree())""" res = cls._grobtree(**kwargs) return res class Axis(GTree): pass class XAxis(Axis): _xaxis = xaxis _xaxisgrob = grid.xaxisGrob @classmethod def xaxis(cls, **kwargs): """ Constructor (uses the R function grid::xaxis())""" res = cls._xaxis(**kwargs) return res @classmethod def xaxisgrob(cls, **kwargs): """ Constructor (uses the R function grid::xaxisgrob())""" res = cls._xaxisgrob(**kwargs) return res class YAxis(Axis): _yaxis = yaxis _yaxisgrob = grid.yaxisGrob @classmethod def yaxis(cls, **kwargs): """ Constructor (uses the R function grid::yaxis())""" res = cls._yaxis(**kwargs) return res @classmethod def yaxisgrob(cls, **kwargs): """ Constructor (uses the R function grid::yaxisgrob())""" res = cls._yaxisgrob(**kwargs) return res class Viewport(robjects.RObject): """ Drawing context. Viewports can be thought of as nodes in a scene graph. """ _pushviewport = grid_env['pushViewport'] _popviewport = grid_env['popViewport'] _current = grid_env['current.viewport'] _plotviewport = grid_env['plotViewport'] _downviewport = grid_env['downViewport'] _seek = grid_env['seekViewport'] _upviewport = grid_env['upViewport'] _viewport = grid_env['viewport'] def push(self, recording=True): self._pushviewport(self, recording=recording) @classmethod def pop(cls, n): """ Pop n viewports from the stack. """ cls._popviewport(n) @classmethod def current(cls): """ Return the current viewport in the stack. """ cls._current() @classmethod def default(cls, **kwargs): cls._plotviewport(**kwargs) @classmethod def down(cls, name, strict=False, recording=True): """ Return the number of Viewports it went down """ cls._downviewport(name, strict=strict, recording=recording) @classmethod def seek(cls, name, recording=True): """ Seek and return a Viewport given its name """ cls._seek(name, recording=recording) @classmethod def up(cls, n, recording=True): """ Go up n viewports """ cls._downviewport(n, recording=recording) @classmethod def viewport(cls, **kwargs): """ Constructor: create a Viewport """ res = cls._viewport(**kwargs) res = cls(res) return res viewport = Viewport.viewport _grid_dict = { 'gpar': Gpar, 'grob': Grob, 'gTree': GTree, 'unit': Unit, 'xaxis': XAxis, 'yaxis': YAxis, 'viewport': Viewport } original_py2rpy = None original_rpy2py = None def grid_rpy2py(robj): pyobj = original_rpy2py(robj) if not isinstance(pyobj, robjects.RS4): rcls = pyobj.rclass if rcls is NULL: rcls = (None, ) try: cls = _grid_dict[rcls[0]] pyobj = cls(pyobj) except KeyError: pass return pyobj def activate(): global original_py2rpy, original_rpy2py # If module is already activated, there is nothing to do if original_py2rpy: return original_py2rpy = conversion.py2rpy original_rpy2py = conversion.rpy2py conversion.rpy2py = grid_rpy2py def deactivate(): global original_py2rpy, original_rpy2py # If module has never been activated or already deactivated, # there is nothing to do if not original_py2rpy: return conversion.py2rpy = original_py2rpy conversion.rpy2py = original_rpy2py original_py2rpy = original_rpy2py = None rpy2-3.2.6/rpy2/robjects/constants.py0000644000175000017500000000040013576515767020701 0ustar laurentlaurent00000000000000""" R objects with fixed values. """ import rpy2.rinterface as rinterface _reval = rinterface.baseenv['eval'] # NULL NULL = _reval(rinterface.parse("NULL")) # TRUE/FALSE TRUE = _reval(rinterface.parse("TRUE")) FALSE = _reval(rinterface.parse("FALSE")) rpy2-3.2.6/rpy2/robjects/methods.py0000644000175000017500000002507713576515767020351 0ustar laurentlaurent00000000000000from types import SimpleNamespace from rpy2.robjects.robject import RObjectMixin import rpy2.rinterface as rinterface from rpy2.rinterface import StrSexpVector from rpy2.robjects import help as rhelp from rpy2.robjects import conversion _get_exported_value = rinterface.baseenv['::'] getmethod = _get_exported_value('methods', 'getMethod') require = rinterface.baseenv.find('require') require(StrSexpVector(('methods', )), quiet=rinterface.BoolSexpVector((True, ))) class RS4(RObjectMixin, rinterface.SexpS4): """ Python representation of an R instance of class 'S4'. """ def slotnames(self): """ Return the 'slots' defined for this object """ return methods_env['slotNames'](self) def do_slot(self, name): return conversion.rpy2py(super(RS4, self).do_slot(name)) @staticmethod def isclass(name): """ Return whether the given name is a defined class. """ name = conversion.py2rpy(name) return methods_env['isClass'](name)[0] def validobject(self, test=False, complete=False): """ Return whether the instance is 'valid' for its class. """ test = conversion.py2rpy(test) complete = conversion.py2rpy(complete) return methods_env['validObject'](self, test=test, complete=complete)[0] class ClassRepresentation(RS4): """ Definition of an R S4 class """ slots = property(lambda x: [y[0] for y in x.do_slot('slots')], None, None, "Slots (attributes) for the class") basenames = property(lambda x: [y[0] for y in x.do_slot('contains')], None, None, "Parent classes") contains = basenames isabstract = property(lambda x: x.do_slot('virtual')[0], None, None, "Is the class an abstract class ?") virtual = isabstract packagename = property(lambda x: x.do_slot('package')[0], None, None, "R package in which the class is defined") package = packagename classname = property(lambda x: x.do_slot('className')[0], None, None, "Name of the R class") def getclassdef(cls_name, cls_packagename): cls_def = methods_env['getClassDef'](StrSexpVector((cls_name,)), StrSexpVector((cls_packagename, ))) cls_def = ClassRepresentation(cls_def) cls_def.__rname__ = cls_name return cls_def class RS4_Type(type): def __new__(mcs, name, bases, cls_dict): try: cls_rname = cls_dict['__rname__'] except KeyError: cls_rname = name try: accessors = cls_dict['__accessors__'] except KeyError: accessors = [] for rname, where, \ python_name, as_property, \ docstring in accessors: if where is None: where = rinterface.globalenv else: where = StrSexpVector(('package:%s' % where, )) if python_name is None: python_name = rname signature = StrSexpVector((cls_rname, )) r_meth = getmethod(StrSexpVector((rname, )), signature=signature, where=where) r_meth = conversion.rpy2py(r_meth) if as_property: cls_dict[python_name] = property(r_meth, None, None, doc=docstring) else: cls_dict[python_name] = lambda self: r_meth(self) return type.__new__(mcs, name, bases, cls_dict) # playground to experiment with more metaclass-level automation class RS4Auto_Type(type): """ This type (metaclass) takes an R S4 class and create a Python class out of it, copying the R documention page into the Python docstring. A class with this metaclass has the following optional attributes: __rname__, __rpackagename__, __attr__translation, __meth_translation__. """ def __new__(mcs, name, bases, cls_dict): try: cls_rname = cls_dict['__rname__'] except KeyError: cls_rname = name try: cls_rpackagename = cls_dict['__rpackagename__'] except KeyError: cls_rpackagename = None try: cls_attr_translation = cls_dict['__attr_translation__'] except KeyError: cls_attr_translation = {} try: cls_meth_translation = cls_dict['__meth_translation__'] except KeyError: cls_meth_translation = {} cls_def = getclassdef(cls_rname, cls_rpackagename) # documentation / help if cls_rpackagename is None: cls_dict['__doc__'] = "Undocumented class from the R workspace." else: pack_help = rhelp.Package(cls_rpackagename) page_help = None try: # R's classes are sometimes documented with a prefix 'class.' page_help = pack_help.fetch('%s-class' % cls_def.__rname__) except rhelp.HelpNotFoundError: pass if page_help is None: try: page_help = pack_help.fetch(cls_def.__rname__) except rhelp.HelpNotFoundError: pass if page_help is None: cls_dict['__doc__'] = ('Unable to fetch R documentation ' 'for the class') else: cls_dict['__doc__'] = ''.join(page_help.to_docstring()) for slt_name in cls_def.slots: # TODO: sanity check on the slot name try: slt_name = cls_attr_translation[slt_name] except KeyError: # no translation: abort pass # TODO: isolate the slot documentation and have it here cls_dict[slt_name] = property(lambda self: self.do_slot(slt_name), None, None, None) # Now tackle the methods all_generics = methods_env['getGenerics']() findmethods = methods_env['findMethods'] # does not seem elegant, but there is probably nothing else to do # than loop across all generics r_cls_rname = StrSexpVector((cls_rname, )) for funcname in all_generics: all_methods = findmethods(StrSexpVector((funcname, )), classes=r_cls_rname) # skip if no methods (issue #301). R's findMethods() result # does not have an attribute "names" if of length zero. if len(all_methods) == 0: continue # all_methods contains all method/signature pairs # having the class we are considering somewhere in the signature # (the R/S4 systems allows multiple dispatch) for name, meth in zip(all_methods.do_slot("names"), all_methods): # R/S4 is storing each method/signature as a string, # with the argument type separated by the character '#' # We will re-use that name for the Python name # (no multiple dispatch in python, the method name # will not be enough), replacing the '#'s with '__'s. signature = name.split("#") meth_name = '__'.join(signature) # function names ending with '<-' indicate that the function # is a setter of some sort. We reflect that by adding a 'set_' # prefix to the Python name (and of course remove the # suffix '<-'). if funcname.endswith('<-'): meth_name = 'set_%s__%s' % (funcname[:-2], meth_name) else: meth_name = '%s__%s' % (funcname, meth_name) # finally replace remaining '.'s in the Python name with '_'s meth_name = meth_name.replace('.', '_') # TODO: sanity check on the function name try: meth_name = cls_meth_translation[meth_name] except KeyError: # no translation: abort pass # TODO: isolate the slot documentation and have it here if meth_name in cls_dict: raise Exception("Duplicated attribute/method name.") cls_dict[meth_name] = meth return type.__new__(mcs, name, bases, cls_dict) def set_accessors(cls, cls_name, where, acs): # set accessors (to be abandonned for the metaclass above ?) if where is None: where = rinterface.globalenv else: where = "package:" + str(where) where = StrSexpVector((where, )) for r_name, python_name, as_property, docstring in acs: if python_name is None: python_name = r_name r_meth = getmethod(StrSexpVector((r_name, )), signature=StrSexpVector((cls_name, )), where=where) r_meth = conversion.rpy2py(r_meth) if as_property: setattr(cls, python_name, property(r_meth, None, None)) else: setattr(cls, python_name, lambda self: r_meth(self)) def get_classnames(packname): res = methods_env['getClasses']( where=StrSexpVector(("package:%s" % packname, )) ) return tuple(res) # Namespace to store the definition of RS4 classes rs4classes = SimpleNamespace() def _getclass(rclsname): if hasattr(rs4classes, rclsname): rcls = getattr(rs4classes, rclsname) else: # dynamically create a class rcls = type(rclsname, (RS4, ), dict()) setattr(rs4classes, rclsname, rcls) return rcls def rs4instance_factory(robj): """ Return an RS4 objects (R objects in the 'S4' class system) as a Python object of type inheriting from `robjects.methods.RS4`. The types are located in the namespace `robjects.methods.rs4classes`, and a dummy type is dynamically created whenever necessary. """ clslist = None if len(robj.rclass) > 1: raise ValueError( 'Currently unable to handle more than one class per object' ) for rclsname in robj.rclass: rcls = _getclass(rclsname) return rcls(robj) if clslist is None: return robj methods_env = rinterface.baseenv.find('as.environment')( StrSexpVector(('package:methods', )) ) rpy2-3.2.6/rpy2/robjects/help.py0000644000175000017500000003107613576515767017632 0ustar laurentlaurent00000000000000""" R help system. """ import os from collections import namedtuple import sqlite3 import rpy2.rinterface as rinterface from rpy2.rinterface import StrSexpVector from rpy2.robjects.packages_utils import (get_packagepath, _libpaths, _packages) from collections import OrderedDict tmp = rinterface.baseenv['R.Version']() tmp_major = int(tmp[tmp.do_slot('names').index('major')][0]) tmp_minor = float(tmp[tmp.do_slot('names').index('minor')][0]) readRDS = rinterface.baseenv['readRDS'] del(tmp) del(tmp_major) del(tmp_minor) _eval = rinterface.baseenv['eval'] def quiet_require(name, lib_loc=None): """ Load an R package /quietly/ (suppressing messages to the console). """ if lib_loc is None: lib_loc = "NULL" expr_txt = ('suppressPackageStartupMessages(base::require(%s, lib.loc=%s))' % (name, lib_loc)) expr = rinterface.parse(expr_txt) ok = _eval(expr) return ok quiet_require('tools') _get_namespace = rinterface.baseenv['getNamespace'] _lazyload_dbfetch = rinterface.baseenv['lazyLoadDBfetch'] tools_ns = _get_namespace(StrSexpVector(('tools',))) _Rd_db = tools_ns['Rd_db'] _Rd_deparse = tools_ns['.Rd_deparse'] __rd_meta = os.path.join('Meta', 'Rd.rds') __package_meta = os.path.join('Meta', 'package.rds') def create_metaRd_db(dbcon): """ Create an database to store R help pages. dbcon: database connection (assumed to be SQLite - may or may not work with other databases) """ dbcon.execute(''' CREATE TABLE package ( name TEXT UNIQUE, title TEXT, version TEXT, description TEXT ); ''') dbcon.execute(''' CREATE TABLE rd_meta ( id INTEGER, file TEXT UNIQUE, name TEXT, type TEXT, title TEXT, encoding TEXT, package_rowid INTEGER ); ''') dbcon.execute(''' CREATE INDEX type_idx ON rd_meta (type); ''') dbcon.execute(''' CREATE TABLE rd_alias_meta ( rd_meta_rowid INTEGER, alias TEXT ); ''') dbcon.execute(''' CREATE INDEX alias_idx ON rd_alias_meta (alias); ''') dbcon.commit() def populate_metaRd_db(package_name, dbcon, package_path=None): """ Populate a database with the meta-information associated with an R package: version, description, title, and aliases (those are what the R help system is organised around). - package_name: a string - dbcon: a database connection - package_path: path the R package installation (default: None) """ if package_path is None: package_path = get_packagepath(package_name) rpath = StrSexpVector((os.path.join(package_path, __package_meta),)) rds = readRDS(rpath) desc = rds[rds.do_slot('names').index('DESCRIPTION')] db_res = dbcon.execute('insert into package values (?,?,?,?)', (desc[desc.do_slot('names').index('Package')], desc[desc.do_slot('names').index('Title')], desc[desc.do_slot('names').index('Version')], desc[desc.do_slot('names').index('Description')], )) package_rowid = db_res.lastrowid rpath = StrSexpVector((os.path.join(package_path, __rd_meta),)) rds = readRDS(rpath) FILE_I = rds.do_slot("names").index('File') NAME_I = rds.do_slot("names").index('Name') TYPE_I = rds.do_slot("names").index('Type') TITLE_I = rds.do_slot("names").index('Title') ENCODING_I = rds.do_slot("names").index('Encoding') ALIAS_I = rds.do_slot("names").index('Aliases') for row_i in range(len(rds[0])): db_res = dbcon.execute('insert into rd_meta values (?,?,?,?,?,?,?)', (row_i, rds[FILE_I][row_i], rds[NAME_I][row_i], rds[TYPE_I][row_i], rds[TITLE_I][row_i], rds[ENCODING_I][row_i], package_rowid)) rd_rowid = db_res.lastrowid for alias in rds[ALIAS_I][row_i]: dbcon.execute('insert into rd_alias_meta values (?,?)', (rd_rowid, alias)) Item = namedtuple('Item', 'name value') class Page(object): """ An R documentation page. The original R structure is a nested sequence of components, corresponding to the latex-like .Rd file An help page is divided into sections, the names for the sections are the keys for the dict attribute 'sections', and a given section can be extracted with the square-bracket operator. In R, the S3 class 'Rd' is the closest entity to this class. """ def __init__(self, struct_rdb, _type=''): sections = OrderedDict() for elt in struct_rdb: rd_tag = elt.do_slot("Rd_tag")[0][1:] if rd_tag == 'section': rd_section = rd_tag[0] lst = sections.get(rd_tag) if lst is None: lst = [] sections[rd_tag] = lst for sub_elt in elt: lst.append(sub_elt) self._sections = sections self._type = _type def _section_get(self): return self._sections sections = property(_section_get, None, "Sections in the in help page, as a dict.") def __getitem__(self, item): """ Get a section """ return self.sections[item] def arguments(self): """ Get the arguments and their description as a list of tuples. """ section = self._sections.get('arguments') res = list() if section is None: return res for item in section: if item.do_slot("Rd_tag")[0] == '\\item': if len(item) != 2: continue arg_name = _Rd_deparse(item[0])[0] arg_desc = _Rd_deparse(item[1])[0] for x in arg_name.split(','): x = x.lstrip() if x.endswith('\\dots'): x = '...' res.append(Item(x, arg_desc)) else: continue return res def description(self): """ Get the description of the entry """ section = self._sections.get('description', None) if section is None: return '' else: res = ''.join(_Rd_deparse(x)[0] for x in section) return res def title(self): """ Get the title """ section = self._sections['title'] res = ''.join(_Rd_deparse(x)[0] for x in section) return res def value(self): """ Get the value returned """ section = self._sections.get('value', None) if section is None: return '' else: res = ''.join(_Rd_deparse(x)[0] for x in section) return res def seealso(self): """ Get the other documentation entries recommended """ section = self._sections.get('seealso', None) if section is None: return '' else: res = ''.join(_Rd_deparse(x)[0] for x in section) return res def usage(self): """ Get the usage for the object """ section = self._sections.get('usage', None) if section is None: res = '' else: res = (_Rd_deparse(x)[0] for x in section) res = ''.join(res) return res def iteritems(self): """ iterator through the sections names and content in the documentation Page. """ return self.sections.iteritems def to_docstring(self, section_names=None): """ section_names: list of section names to consider. If None all sections are used. Returns a string that can be used as a Python docstring. """ s = [] if section_names is None: section_names = self.sections.keys() def walk(tree): if not isinstance(tree, str): for elt in tree: walk(elt) else: s.append(tree) s.append(' ') for name in section_names: s.append(name) s.append(os.linesep) s.append('-' * len(name)) s.append(os.linesep) s.append(os.linesep) walk(self.sections[name]) s.append(os.linesep) s.append(os.linesep) return ''.join(s) class Package(object): """ The R documentation page (aka help) for a package. """ __package_path = None __package_name = None __aliases_info = 'aliases.rds' __hsearch_meta = os.path.join('Meta', 'hsearch.rds') __paths_info = 'paths.rds' __anindex_info = 'AnIndex' def __package_name_get(self): return self.__package_name name = property(__package_name_get, None, None, 'Name of the package as known by R') def __init__(self, package_name, package_path=None): self.__package_name = package_name if package_path is None: package_path = get_packagepath(package_name) self.__package_path = package_path rd_meta_dbcon = sqlite3.connect(':memory:') create_metaRd_db(rd_meta_dbcon) populate_metaRd_db(package_name, rd_meta_dbcon, package_path=package_path) self._dbcon = rd_meta_dbcon path = os.path.join(package_path, 'help', package_name + '.rdx') self._rdx = readRDS(StrSexpVector((path, ))) def fetch(self, alias): """ Fetch the documentation page associated with a given alias. For S4 classes, the class name is *often* suffixed with '-class'. For example, the alias to the documentation for the class AnnotatedDataFrame in the package Biobase is 'AnnotatedDataFrame-class'. """ c = self._dbcon.execute( 'SELECT rd_meta_rowid, alias FROM rd_alias_meta WHERE alias=?', (alias, ) ) res_alias = c.fetchall() if len(res_alias) == 0: raise HelpNotFoundError( 'No help could be fetched', topic=alias, package=self.__package_name ) c = self._dbcon.execute( 'SELECT file, name, type FROM rd_meta WHERE rowid=?', (res_alias[0][0], ) ) # since the selection is on a verified rowid we are sure to # exactly get one row res = c.fetchall() rkey = StrSexpVector((res[0][0][:-3], )) _type = res[0][2] rpath = StrSexpVector((os.path.join(self.package_path, 'help', self.__package_name + '.rdb'),)) rdx_variables = ( self._rdx[self._rdx.do_slot('names').index('variables')] ) _eval = rinterface.baseenv['eval'] devnull_func = rinterface.parse('function(x) {}') devnull_func = _eval(devnull_func) res = _lazyload_dbfetch( rdx_variables[rdx_variables.do_slot('names').index(rkey[0])], rpath, self._rdx[self._rdx.do_slot('names').index("compressed")], devnull_func ) p_res = Page(res, _type=_type) return p_res package_path = property(lambda self: str(self.__package_path), None, None, 'Path to the installed R package') def __repr__(self): r = 'R package %s %s' % (self.__package_name, super(Package, self).__repr__()) return r class HelpNotFoundError(KeyError): """ Exception raised when an help topic cannot be found. """ def __init__(self, msg, topic=None, package=None): super(HelpNotFoundError, self).__init__(msg) self.topic = topic self.package = package def pages(topic): """ Get help pages corresponding to a given topic. """ res = list() for path in _libpaths(): for name in _packages(**{'all.available': True, 'lib.loc': StrSexpVector((path,))}): # TODO: what if the same package is installed # at different locations ? pack = Package(name) try: page = pack.fetch(topic) res.append(page) except HelpNotFoundError as hnfe: pass return tuple(res) def docstring(package, alias, sections=['usage', 'arguments']): if not isinstance(package, Package): package = Package(package) page = package.fetch(alias) return page.to_docstring(sections) rpy2-3.2.6/rpy2/robjects/__init__.py0000644000175000017500000002547113615570032020417 0ustar laurentlaurent00000000000000""" R objects as Python objects. The module is structured around the singleton r of class R, that represents an embedded R. License: GPLv2+ """ import os import types import array import rpy2.rinterface as rinterface import rpy2.rlike.container as rlc from rpy2.robjects.robject import RObjectMixin, RObject import rpy2.robjects.functions from rpy2.robjects.environments import Environment from rpy2.robjects.methods import RS4 from . import conversion from . import vectors from rpy2.rinterface import (Sexp, SexpVector, SexpClosure, SexpEnvironment, SexpS4, StrSexpVector, SexpExtPtr) from rpy2.robjects.functions import Function from rpy2.robjects.functions import SignatureTranslatedFunction _globalenv = rinterface.globalenv _reval = rinterface.baseenv['eval'] BoolVector = vectors.BoolVector IntVector = vectors.IntVector FloatVector = vectors.FloatVector ComplexVector = vectors.ComplexVector StrVector = vectors.StrVector FactorVector = vectors.FactorVector Vector = vectors.Vector ListVector = vectors.ListVector DateVector = vectors.DateVector POSIXct = vectors.POSIXct POSIXlt = vectors.POSIXlt Array = vectors.Array Matrix = vectors.Matrix DataFrame = vectors.DataFrame # Missing values. NA_Real = rinterface.NA_Real NA_Integer = rinterface.NA_Integer NA_Logical = rinterface.NA_Logical NA_Character = rinterface.NA_Character NA_Complex = rinterface.NA_Complex NULL = rinterface.NULL def reval(string, envir=_globalenv): """ Evaluate a string as R code - string: a string - envir: an environment in which the environment should take place (default: R's global environment) """ p = rinterface.parse(string) res = _reval(p, envir=envir) return res default_converter = conversion.Converter('base empty converter') @default_converter.rpy2py.register(RObject) def _rpy2py_robject(obj): return obj def _vector_matrix_array(obj, vector_cls, matrix_cls, array_cls): # Should it be promoted to array or matrix ? try: dim = obj.do_slot("dim") if len(dim) == 2: return matrix_cls else: return array_cls except Exception: return vector_cls def sexpvector_to_ro(obj): if not isinstance(obj, rinterface.SexpVector): raise ValueError('%s is not an R vector.' % obj) rcls = obj.rclass if 'data.frame' in rcls: cls = vectors.DataFrame # TODO: There no case/switch statement in Python, but may be # there is a more elegant way to implement this. elif obj.typeof == rinterface.RTYPES.INTSXP: if 'factor' in rcls: cls = vectors.FactorVector else: cls = _vector_matrix_array(obj, vectors.IntVector, vectors.IntMatrix, vectors.IntArray) elif obj.typeof == rinterface.RTYPES.REALSXP: if vectors.POSIXct.isrinstance(obj): cls = vectors.POSIXct else: cls = _vector_matrix_array(obj, vectors.FloatVector, vectors.FloatMatrix, vectors.FloatArray) elif obj.typeof == rinterface.RTYPES.LGLSXP: cls = _vector_matrix_array(obj, vectors.BoolVector, vectors.BoolMatrix, vectors.BoolArray) elif obj.typeof == rinterface.RTYPES.STRSXP: cls = _vector_matrix_array(obj, vectors.StrVector, vectors.StrMatrix, vectors.StrArray) elif obj.typeof == rinterface.RTYPES.VECSXP: cls = vectors.ListVector elif obj.typeof == rinterface.RTYPES.LISTSXP: cls = rinterface.PairlistSexpVector elif obj.typeof == rinterface.RTYPES.LANGSXP and 'formula' in rcls: cls = Formula elif obj.typeof == rinterface.RTYPES.CPLXSXP: cls = _vector_matrix_array(obj, vectors.ComplexVector, vectors.ComplexMatrix, vectors.ComplexArray) elif obj.typeof == rinterface.RTYPES.RAWSXP: cls = _vector_matrix_array(obj, vectors.ByteVector, vectors.ByteMatrix, vectors.ByteArray) else: cls = None if cls is not None: return cls(obj) else: return obj default_converter.rpy2py.register(SexpVector, sexpvector_to_ro) TYPEORDER = {bool: (0, BoolVector), int: (1, IntVector), float: (2, FloatVector), complex: (3, ComplexVector), str: (4, StrVector)} def sequence_to_vector(lst): curr_typeorder = -1 i = None for i, elt in enumerate(lst): cls = type(elt) if cls in TYPEORDER: if TYPEORDER[cls][0] > curr_typeorder: curr_typeorder, curr_type = TYPEORDER[cls] else: raise ValueError('The element %i in the list has a type ' 'that cannot be handled.' % i) if i is None: raise ValueError('The parameter "lst" is an empty sequence. ' 'The type of the corresponding R vector cannot ' 'be determined.') res = curr_type(lst) return res @default_converter.py2rpy.register(rinterface._MissingArgType) def _py2rpy_missingargtype(obj): return obj @default_converter.py2rpy.register(bool) def _py2rpy_bool(obj): return obj @default_converter.py2rpy.register(int) def _py2rpy_int(obj): return obj @default_converter.py2rpy.register(float) def _py2rpy_float(obj): return obj @default_converter.py2rpy.register(bytes) def _py2rpy_bytes(obj): return obj @default_converter.py2rpy.register(str) def _py2rpy_str(obj): return obj @default_converter.rpy2py.register(SexpClosure) def _rpy2py_sexpclosure(obj): return SignatureTranslatedFunction(obj) @default_converter.rpy2py.register(SexpEnvironment) def _rpy2py_sexpenvironment(obj): return Environment(obj) @default_converter.rpy2py.register(SexpS4) def _rpy2py_sexps4(obj): return RS4(obj) @default_converter.rpy2py.register(SexpExtPtr) def _rpy2py_sexpextptr(obj): return obj @default_converter.rpy2py.register(object) def _rpy2py_object(obj): return RObject(obj) @default_converter.rpy2py.register(type(NULL)) def _rpy2py_null(obj): return obj # TODO: delete ? def default_py2ri(o): """ Convert an arbitrary Python object to a :class:`rpy2.rinterface.Sexp` object. Creates an R object with the content of the Python object, wich means data copying. :param o: object :rtype: :class:`rpy2.rinterface.Sexp` (and subclasses) """ pass @default_converter.py2rpy.register(RObject) def _py2rpy_robject(obj): return rinterface.Sexp(obj) @default_converter.py2rpy.register(Sexp) def _py2rpy_sexp(obj): return obj @default_converter.py2rpy.register(array.array) def _py2rpy_array(obj): if obj.typecode in ('h', 'H', 'i', 'I'): res = IntVector(obj) elif obj.typecode in ('f', 'd'): res = FloatVector(obj) else: raise( ValueError('Nothing can be done for this array ' 'type at the moment.') ) return res default_converter.py2rpy.register(int, lambda x: x) @default_converter.py2rpy.register(list) def _py2rpy_list(obj): return vectors.ListVector( rinterface.ListSexpVector( [conversion.py2rpy(x) for x in obj] ) ) @default_converter.py2rpy.register(rlc.TaggedList) def _py2rpy_taggedlist(obj): res = vectors.ListVector( rinterface.ListSexpVector([conversion.py2rpy(x) for x in obj]) ) res.do_slot_assign('names', rinterface.StrSexpVector(obj.tags)) return res @default_converter.py2rpy.register(complex) def _py2rpy_complex(obj): return obj @default_converter.py2rpy.register(types.FunctionType) def _function_to_rpy(func): def wrap(*args): res = func(*args) res = conversion.py2ro(res) return res rfunc = rinterface.rternalize(wrap) return conversion.rpy2py(rfunc) @default_converter.rpy2py.register(object) def _(obj): return obj class Formula(RObjectMixin, rinterface.Sexp): def __init__(self, formula, environment=_globalenv): if isinstance(formula, str): inpackage = rinterface.baseenv["::"] asformula = inpackage(rinterface.StrSexpVector(['stats', ]), rinterface.StrSexpVector(['as.formula', ])) formula = rinterface.StrSexpVector([formula, ]) robj = asformula(formula, env=environment) else: robj = formula super(Formula, self).__init__(robj) def getenvironment(self): """ Get the environment in which the formula is finding its symbols.""" res = self.do_slot(".Environment") res = conversion.rpy2py(res) return res def setenvironment(self, val): """ Set the environment in which a formula will find its symbols.""" if not isinstance(val, rinterface.SexpEnvironment): raise TypeError("The environment must be an instance of" + " rpy2.rinterface.Sexp.environment") self.do_slot_assign(".Environment", val) environment = property(getenvironment, setenvironment, "R environment in which the formula will look for" + " its variables.") class R(object): """ Singleton representing the embedded R running. """ _instance = None def __new__(cls): if cls._instance is None: rinterface.initr() cls._instance = object.__new__(cls) return cls._instance def __getattribute__(self, attr): try: return super(R, self).__getattribute__(attr) except AttributeError as ae: orig_ae = str(ae) try: return self.__getitem__(attr) except LookupError: raise AttributeError(orig_ae) def __getitem__(self, item): res = _globalenv.find(item) res = conversion.rpy2py(res) if hasattr(res, '__rname__'): res.__rname__ = item return res # TODO: check that this is properly working def __cleanup__(self): rinterface.endEmbeddedR() del(self) def __str__(self): version = self['version'] s = [super(R, self).__str__()] s.extend('%s: %s' % (n, val[0]) for n, val in zip(version.names, version)) return os.linesep.join(s) def __call__(self, string): p = rinterface.parse(string) res = self.eval(p) return conversion.rpy2py(res) r = R() conversion.set_conversion(default_converter) globalenv = conversion.converter.rpy2py(_globalenv) baseenv = conversion.converter.rpy2py(rinterface.baseenv) emptyenv = conversion.converter.rpy2py(rinterface.emptyenv) rpy2-3.2.6/rpy2/robjects/language.py0000644000175000017500000000121113576515767020451 0ustar laurentlaurent00000000000000""" Utilities for manipulating or evaluating the R language. """ import rpy2.robjects.conversion as conversion import rpy2.rinterface as ri _reval = ri.baseenv['eval'] _parse = ri.parse def eval(x, envir=ri.globalenv): """ Evaluate R code. If the input object is an R expression it evaluates it directly, if it is a string it parses it before evaluating it. By default the evaluation is performed in R's global environment but a specific environment can be specified.""" if isinstance(x, str): p = _parse(x) else: p = x res = _reval(p, envir=envir) res = conversion.rpy2py(res) return res rpy2-3.2.6/rpy2/robjects/conversion.py0000644000175000017500000001067313576515767021067 0ustar laurentlaurent00000000000000""" The module contains the conversion functions to be used by the rpy2.robjects functions and methods. These functions are initially empty place-holders, raising a NotImplementedError exception. """ from functools import singledispatch import rpy2.rinterface_lib.sexp import rpy2.rinterface_lib.conversion def noconversion(obj): """ Bypass robject-level conversion. Bypass robject-level conversion, casting the object down to rinterface-level rpy2 objects. :param obj: Any object :return: Either an rinterface-leve object or a Python object. """ if isinstance(obj, rpy2.rinterface_lib.sexp.Sexp): res = (rpy2.rinterface_lib.conversion ._sexpcapsule_to_rinterface(obj.__sexp__)) else: res = obj return res def overlay_converter(src, target): """ :param src: source of additional conversion rules :type src: :class:`Converter` :param target: target. The conversion rules in the src will be added to this object. :type target: :class:`Converter` """ for k, v in src.py2rpy.registry.items(): # skip the root dispatch if k is object and v is _py2rpy: continue target._py2rpy.register(k, v) for k, v in src.rpy2py.registry.items(): # skip the root dispatch if k is object and v is _rpy2py: continue target._rpy2py.register(k, v) def _py2rpy(obj): """ Dummy function for py2rpy. This function will convert Python objects into rpy2.rinterface objects. """ raise NotImplementedError( "Conversion 'py2rpy' not defined for objects of type '%s'" % str(type(obj)) ) def _rpy2py(obj): """ Dummy function for rpy2py. This function will convert Python objects into Python (presumably non-rpy2) objects. """ raise NotImplementedError( "Conversion 'rpy2py' not defined for objects of type '%s'" % str(type(obj)) ) class Converter(object): """ Conversion between rpy2's low-level and high-level proxy objects for R objects, and Python (no R) objects. Converter objects can be added, the result being a Converter objects combining the translation rules from the different converters. """ name = property(lambda self: self._name) py2rpy = property(lambda self: self._py2rpy) rpy2py = property(lambda self: self._rpy2py) lineage = property(lambda self: self._lineage) def __init__(self, name, template=None): (py2rpy, rpy2py) = Converter.make_dispatch_functions() self._name = name self._py2rpy = py2rpy self._rpy2py = rpy2py if template is None: lineage = tuple() else: lineage = list(template.lineage) lineage.append(name) lineage = tuple(lineage) overlay_converter(template, self) self._lineage = lineage def __add__(self, converter): assert isinstance(converter, Converter) new_name = '%s + %s' % (self.name, converter.name) # create a copy of `self` as the result converter result_converter = Converter(new_name, template=self) overlay_converter(converter, result_converter) return result_converter @staticmethod def make_dispatch_functions(): py2rpy = singledispatch(_py2rpy) rpy2py = singledispatch(_rpy2py) return (py2rpy, rpy2py) class ConversionContext(object): """ Context manager for instances of class Converter. """ def __init__(self, ctx_converter): assert isinstance(ctx_converter, Converter) self._original_converter = converter self.ctx_converter = Converter('Converter-%i-in-context' % id(self), template=ctx_converter) def __enter__(self): set_conversion(self.ctx_converter) return self.ctx_converter def __exit__(self, exc_type, exc_val, exc_tb): set_conversion(self._original_converter) return False localconverter = ConversionContext converter = None py2rpy = None rpy2py = None def set_conversion(this_converter): """ Set conversion rules in the conversion module. :param this_converter: The conversion rules :type this_converter: :class:`Converter` """ global converter, py2rpy, rpy2py converter = this_converter py2rpy = converter.py2rpy rpy2py = converter.rpy2py set_conversion(Converter('base converter')) rpy2-3.2.6/rpy2/robjects/vectors.py0000644000175000017500000013530213615434564020351 0ustar laurentlaurent00000000000000import abc from rpy2.robjects.robject import RObjectMixin import rpy2.rinterface as rinterface from . import conversion import rpy2.rlike.container as rlc import copy import itertools import math import os import jinja2 import time import pytz import tzlocal from datetime import datetime from time import struct_time, mktime, tzname from operator import attrgetter import warnings from rpy2.rinterface import (Sexp, ListSexpVector, StrSexpVector, IntSexpVector, ByteSexpVector, BoolSexpVector, ComplexSexpVector, FloatSexpVector, NA_Real, NA_Integer, NA_Character, NA_Logical, NULL, MissingArg) globalenv_ri = rinterface.globalenv baseenv_ri = rinterface.baseenv r_concat = baseenv_ri['c'] as_character = baseenv_ri['as.character'] utils_ri = baseenv_ri['as.environment']( rinterface.StrSexpVector(("package:utils", )) ) # The default timezone can be used for time or datetime objects. default_timezone = None class ExtractDelegator(object): """ Delegate the R 'extraction' ("[") and 'replacement' ("[<-") of items in a vector or vector-like object. This can help making syntactic niceties possible.""" _extractfunction = rinterface.baseenv['['] _replacefunction = rinterface.baseenv['[<-'] def __init__(self, parent): self._parent = parent def __call__(self, *args, **kwargs): """ Subset the "R-way.", using R's "[" function. In a nutshell, R indexing differs from Python indexing on: - indexing can be done with integers or strings (that are 'names') - an index equal to TRUE will mean everything selected (because of the recycling rule) - integer indexing starts at one - negative integer indexing means exclusion of the given integers - an index is itself a vector of elements to select """ conv_args = [None, ] * (len(args)+1) conv_args[0] = self._parent for i, x in enumerate(args, 1): if x is MissingArg: conv_args[i] = x else: conv_args[i] = conversion.py2rpy(x) kwargs = copy.copy(kwargs) for k, v in kwargs.values(): kwargs[k] = conversion.py2rpy(v) fun = self._extractfunction res = fun(*conv_args, **kwargs) res = conversion.rpy2py(res) return res def __getitem__(self, item): res = self(item) return res def __setitem__(self, item, value): """ Assign a given value to a given index position in the vector. The index position can either be: - an int: x[1] = y - a tuple of ints: x[1, 2, 3] = y - an item-able object (such as a dict): x[{'i': 1}] = y """ fun = self._replacefunction if type(item) is tuple: args = list([None, ] * (len(item)+2)) for i, v in enumerate(item): if v is MissingArg: continue args[i+1] = conversion.py2rpy(v) args[-1] = conversion.py2rpy(value) args[0] = self._parent res = fun(*args) elif (type(item) is dict) or (type(item) is rlc.TaggedList): args = rlc.TaggedList.from_items(item) for i, (k, v) in enumerate(args.items()): args[i] = conversion.py2rpy(v) args.append(conversion.py2rpy(value), tag=None) args.insert(0, self._parent, tag=None) res = fun.rcall(tuple(args.items()), globalenv_ri) else: args = [self._parent, conversion.py2rpy(item), conversion.py2rpy(value)] res = fun(*args) # TODO: check refcount and copying self._parent.__sexp__ = res.__sexp__ class DoubleExtractDelegator(ExtractDelegator): """ Delegate the R 'extraction' ("[[") and "replacement" ("[[<-") of items in a vector or vector-like object. This can help making syntactic niceties possible.""" _extractfunction = rinterface.baseenv['[['] _replacefunction = rinterface.baseenv['[[<-'] class VectorOperationsDelegator(object): """ Delegate operations such as __getitem__, __add__, etc... to the corresponding R function. This permits a convenient coexistence between operators on Python sequence object with their R conterparts. """ def __init__(self, parent): """ The parent in expected to inherit from Vector. """ self._parent = parent def __add__(self, x): res = globalenv_ri.find('+')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __sub__(self, x): res = globalenv_ri.find('-')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __matmul__(self, x): res = globalenv_ri.find("%*%")(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __mul__(self, x): res = globalenv_ri.find('*')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __pow__(self, x): res = globalenv_ri.find('^')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __floordiv__(self, x): res = globalenv_ri.find('%/%')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __truediv__(self, x): res = globalenv_ri.find('/')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __mod__(self, x): res = globalenv_ri.find('%%')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __or__(self, x): res = globalenv_ri.find('|')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __and__(self, x): res = globalenv_ri.find('&')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __invert__(self): res = globalenv_ri.find('!')(self._parent) return conversion.rpy2py(res) # Comparisons def __lt__(self, x): res = globalenv_ri.find('<')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __le__(self, x): res = globalenv_ri.find('<=')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __eq__(self, x): res = globalenv_ri.find('==')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __ne__(self, x): res = globalenv_ri.find('!=')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __gt__(self, x): res = globalenv_ri.find('>')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __ge__(self, x): res = globalenv_ri.find('>=')(self._parent, conversion.py2rpy(x)) return conversion.rpy2py(res) def __neg__(self): res = globalenv_ri.find('-')(self._parent) return res def __contains__(self, what): res = globalenv_ri.find('%in%')(what, self._parent) return res[0] class Vector(RObjectMixin): """Vector(seq) -> Vector. The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python object. In the later case, a conversion will be attempted using conversion.py2rpy(). R vector-like object. Items can be accessed with: - the method "__getitem__" ("[" operator) - the delegators rx or rx2 """ _sample = rinterface.baseenv['sample'] _html_template = jinja2.Template( """ {{ classname }} with {{ nelements }} elements. {% for elt in elements %} {% endfor %}
{{ elt }}
""") def _add_rops(self): self.ro = VectorOperationsDelegator(self) self.rx = ExtractDelegator(self) self.rx2 = DoubleExtractDelegator(self) def __add__(self, x): res = baseenv_ri.find("c")(self, conversion.py2rpy(x)) res = conversion.rpy2py(res) return res def __getitem__(self, i): res = super().__getitem__(i) if isinstance(res, Sexp): res = conversion.rpy2py(res) return res def __setitem__(self, i, value): value = conversion.py2rpy(value) super().__setitem__(i, value) @property def names(self): """Names for the items in the vector.""" res = super().names res = conversion.rpy2py(res) return res @names.setter def names(self, value): res = globalenv_ri.find("names<-")(self, conversion.py2rpy(value)) self.__sexp__ = res.__sexp__ def items(self): """ iterator on names and values """ # TODO: should be a view ? if super().names.rsame(rinterface.NULL): it_names = itertools.cycle((None, )) else: it_names = iter(self.names) it_self = iter(self) for v, k in zip(it_self, it_names): yield (k, v) def sample(self, n, replace: bool = False, probabilities=None): """ Draw a random sample of size n from the vector. If 'replace' is True, the sampling is done with replacement. The optional argument 'probabilities' can indicate sampling probabilities.""" assert isinstance(n, int) assert isinstance(replace, bool) if probabilities is not None: if len(probabilities) != len(self): raise ValueError('The sequence of probabilities must ' 'match the length of the vector.') if not isinstance(probabilities, rinterface.FloatSexpVector): probabilities = FloatVector(probabilities) res = self._sample(self, IntVector((n,)), replace=BoolVector((replace, )), prob=probabilities) res = conversion.rpy2py(res) return res def repr_format_elt(self, elt, max_width=12): max_width = int(max_width) if elt in (NA_Real, NA_Integer, NA_Character, NA_Logical): res = repr(elt) elif isinstance(elt, int): res = '%8i' % elt elif isinstance(elt, float): res = '%8f' % elt else: if isinstance(elt, str): elt = repr(elt) else: elt = type(elt).__name__ if len(elt) < max_width: res = elt else: res = "%s..." % (str(elt[:(max_width - 3)])) return res def _iter_formatted(self, max_items=9): format_elt = self.repr_format_elt ln = len(self) half_items = max_items // 2 if ln == 0: return elif ln < max_items: for elt in conversion.noconversion(self): yield format_elt(elt, max_width=math.floor(52 / ln)) else: for elt in conversion.noconversion(self)[:half_items]: yield format_elt(elt) yield '...' for elt in conversion.noconversion(self)[-half_items:]: yield format_elt(elt) def __repr_content__(self): return ''.join(('[', ', '.join(self._iter_formatted()), ']')) def __repr__(self): return super().__repr__() + os.linesep + \ self.__repr_content__() def _repr_html_(self, max_items=7): d = {'elements': self._iter_formatted(max_items=max_items), 'classname': type(self).__name__, 'nelements': len(self)} html = self._html_template.render(d) return html class StrVector(Vector, StrSexpVector): """Vector of string elements StrVector(seq) -> StrVector. The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python sequence. In the later case, all elements in the sequence should be either strings, or have a str() representation. """ _factorconstructor = rinterface.baseenv['factor'] NAvalue = rinterface.NA_Character def __init__(self, obj): super().__init__(obj) self._add_rops() def factor(self): """ factor() -> FactorVector Construct a factor vector from a vector of strings. """ res = self._factorconstructor(self) return conversion.rpy2py(res) class IntVector(Vector, IntSexpVector): """ Vector of integer elements IntVector(seq) -> IntVector. The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python sequence. In the later case, all elements in the sequence should be either integers, or have an int() representation. """ _tabulate = rinterface.baseenv['tabulate'] NAvalue = rinterface.NA_Integer def __init__(self, obj): super().__init__(obj) self._add_rops() def repr_format_elt(self, elt, max_width=8): max_width = int(max_width) if elt == NA_Integer: res = repr(NA_Integer) else: res = '{:,}'.format(elt) return res def tabulate(self, nbins=None): """ Like the R function tabulate, count the number of times integer values are found """ if nbins is None: nbins = max(1, max(self)) res = self._tabulate(self) return conversion.rpy2py(res) class BoolVector(Vector, BoolSexpVector): """ Vector of boolean (logical) elements BoolVector(seq) -> BoolVector. The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python sequence. In the later case, all elements in the sequence should be either booleans, or have a bool() representation. """ NAvalue = rinterface.NA_Logical def __init__(self, obj): super().__init__(obj) self._add_rops() class ByteVector(Vector, ByteSexpVector): """ Vector of byte elements ByteVector(seq) -> ByteVector. The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python sequence. In the later case, all elements in the sequence should be either be bytes (integers >= 0 and <= 255). """ def __init__(self, obj): super().__init__(obj) self._add_rops() class ComplexVector(Vector, ComplexSexpVector): """ Vector of complex elements ComplexVector(seq) -> ComplexVector The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python sequence. In the later case, all elements in the sequence should be either complex, or have a complex() representation. """ NAvalue = rinterface.NA_Complex def __init__(self, obj): super().__init__(obj) self._add_rops() class FloatVector(Vector, FloatSexpVector): """ Vector of float (double) elements FloatVector(seq) -> FloatVector. The parameter 'seq' can be an instance inheriting from rinterface.SexpVector, or an arbitrary Python sequence. In the later case, all elements in the sequence should be either float, or have a float() representation. """ NAvalue = rinterface.NA_Real def __init__(self, obj): super().__init__(obj) self._add_rops() class FactorVector(IntVector): """ Vector of 'factors'. FactorVector(obj, levels = rinterface.MissingArg, labels = rinterface.MissingArg, exclude = rinterface.MissingArg, ordered = rinterface.MissingArg) -> FactorVector obj: StrVector or StrSexpVector levels: StrVector or StrSexpVector labels: StrVector or StrSexpVector (of same length as levels) exclude: StrVector or StrSexpVector ordered: boolean """ _factor = baseenv_ri['factor'] _levels = baseenv_ri['levels'] _levels_set = baseenv_ri['levels<-'] _nlevels = baseenv_ri['nlevels'] _isordered = baseenv_ri['is.ordered'] NAvalue = rinterface.NA_Integer def __init__(self, obj, levels=rinterface.MissingArg, labels=rinterface.MissingArg, exclude=rinterface.MissingArg, ordered=rinterface.MissingArg): if not isinstance(obj, Sexp): obj = StrSexpVector(obj) if ('factor' in obj.rclass) and \ all(p is rinterface.MissingArg for p in (labels, exclude, ordered)): res = obj else: res = self._factor(obj, levels=levels, labels=labels, exclude=exclude, ordered=ordered) super(FactorVector, self).__init__(res) def repr_format_elt(self, elt, max_width=8): max_width = int(max_width) levels = self._levels(self) if elt is NA_Integer: res = repr(elt) else: res = levels[elt-1] if len(res) >= max_width: res = "%s..." % (res[:(max_width - 3)]) return res def __levels_get(self): res = self._levels(self) return conversion.rpy2py(res) def __levels_set(self, value): res = self._levels_set(self, conversion.py2rpy(value)) self.__sexp__ = res.__sexp__ levels = property(__levels_get, __levels_set) def __nlevels_get(self): res = self._nlevels(self) return res[0] nlevels = property(__nlevels_get, None, None, "number of levels ") def __isordered_get(self): res = self._isordered(self) return res[0] isordered = property(__isordered_get, None, None, "are the levels in the factor ordered ?") def iter_labels(self): """ Iterate the over the labels, that is iterate over the items returning associated label for each item """ levels = self.levels for x in conversion.noconversion(self): yield levels[x-1] class ListVector(Vector, ListSexpVector): """ R list (vector of arbitray elements) ListVector(itemable) -> ListVector. The parameter 'itemable' can be: - an object with a method `items()`, such for example a dict, a rpy2.rlike.container.TaggedList, an rpy2.rinterface.SexpVector of type VECSXP. - an iterable of (name, value) tuples """ _vector = rinterface.baseenv['vector'] _html_template = jinja2.Template( """ {{ classname }} with {{ nelements }} elements. {% for name, elt in names_elements %} {% endfor %}
{{ name }} {{ elt }}
""") def __init__(self, tlist): if isinstance(tlist, rinterface.ListSexpVector): if tlist.typeof != rinterface.RTYPES.VECSXP: raise ValueError("tlist should have " "tlist.typeof == rinterface.RTYPES.VECSXP") super().__init__(tlist) elif hasattr(tlist, 'items') and callable(tlist.items): kv = [(k, conversion.py2rpy(v)) for k, v in tlist.items()] kv = tuple(kv) df = baseenv_ri.find("list").rcall(kv, globalenv_ri) super().__init__(df) elif hasattr(tlist, "__iter__"): if not callable(tlist.__iter__): raise ValueError('tlist should have a /method/ __iter__ ' '(not an attribute)') kv = [(str(k), conversion.py2rpy(v)) for k, v in tlist] kv = tuple(kv) df = baseenv_ri.find("list").rcall(kv, globalenv_ri) super().__init__(df) else: raise ValueError('tlist can only be either an iter-able or an ' 'instance of rpy2.rinterface.ListSexpVector, ' 'of R type VECSXP, or a Python dict.') self._add_rops() def _iter_repr(self, max_items=9): if len(self) <= max_items: for elt in conversion.noconversion(self): yield elt else: half_items = max_items // 2 for i in range(0, half_items): yield self[i] yield '...' for i in range(-half_items, 0): yield self[i] def __repr__(self): res = [] for i, elt in enumerate(self._iter_repr()): if isinstance(elt, ListVector): res.append(super().__repr__()) elif isinstance(elt, str) and elt == '...': res.append(elt) else: try: name = self.names[i] except TypeError: name = '' res.append(" %s: %s%s %s" % (name, type(elt), os.linesep, elt.__repr__())) res = super().__repr__() + os.linesep + \ os.linesep.join(res) return res def _repr_html_(self, max_items=7): elements = list() for e in self._iter_repr(max_items=max_items): if hasattr(e, '_repr_html_'): elements.append(e._repr_html_()) else: elements.append(e) names = list() if len(self) <= max_items: names.extend(self.names) else: half_items = max_items // 2 for i in range(0, half_items): try: name = self.names[i] except TypeError: name = '[no name]' names.append(name) names.append('...') for i in range(-half_items, 0): try: name = self.names[i] except TypeError: name = '[no name]' names.append(name) d = {'names_elements': zip(names, elements), 'nelements': len(self), 'classname': type(self).__name__} html = self._html_template.render(d) return html @staticmethod def from_length(length): """ Create a list of given length """ res = ListVector._vector(StrSexpVector(("list", )), length) res = conversion.rpy2py(res) return res class DateVector(FloatVector): """ Vector of dates """ pass class POSIXt(abc.ABC): """ POSIX time vector. This is an abstract class. """ def repr_format_elt(self, elt, max_width=12): max_width = int(max_width) str_elt = str(elt) if len(str_elt) < max_width: res = elt else: res = "%s..." % str_elt[:(max_width - 3)] return res def _iter_formatted(self, max_items=9): ln = len(self) half_items = max_items // 2 if ln == 0: return elif ln < max_items: str_vec = StrVector(as_character(self)) else: str_vec = r_concat( as_character( self.rx(IntSexpVector(tuple(range(1, (half_items-1))))) ), StrSexpVector(['...']), as_character( self.rx(IntSexpVector(tuple(range((ln-half_items), ln)))) )) for str_elt in str_vec: yield self.repr_format_elt(str_elt) class POSIXlt(POSIXt, ListVector): """ Representation of dates with a 11-component structure (similar to Python's time.struct_time). POSIXlt(seq) -> POSIXlt. The constructor accepts either an R vector or a sequence (an object with the Python sequence interface) of time.struct_time objects. """ _expected_colnames = { x: i for i, x in enumerate( ('sec', 'min', 'hour', 'mday', 'mon', 'year', 'wday', 'yday', 'isdst', 'zone', 'gmtoff')) } # R starts the week on Sunday while Python starts it on Monday _wday_r_to_py = (6, 0, 1, 2, 3, 4, 5) def __init__(self, seq): """ """ if isinstance(seq, Sexp): super()(seq) else: for elt in conversion.noconversion(seq): if not isinstance(elt, struct_time): raise ValueError( 'All elements must inherit from time.struct_time' ) as_posixlt = baseenv_ri['as.POSIXlt'] origin = StrSexpVector([time.strftime("%Y-%m-%d", time.gmtime(0)), ]) rvec = FloatSexpVector([mktime(x) for x in seq]) sexp = as_posixlt(rvec, origin=origin) super().__init__(sexp) def __getitem__(self, i): # "[[" operator returns the components of a time object # (and yes, this is confusing) aslist = ListVector(self) idx = self._expected_colnames seq = (aslist[idx['year']][i]+1900, aslist[idx['mon']][i]+1, aslist[idx['mday']][i], aslist[idx['hour']][i], aslist[idx['min']][i], aslist[idx['sec']][i], self._wday_r_to_py[aslist[idx['wday']][i]], aslist[idx['yday']][i]+1, aslist[idx['isdst']][i]) return struct_time( seq, {'tm_zone': aslist[idx['zone']][i], 'tmp_gmtoff': aslist[idx['gmtoff']][i]} ) def __repr__(self): return super(Sexp, self).__repr__() def get_timezone(): """Return the system's timezone settings.""" if default_timezone: timezone = default_timezone else: timezone = tzlocal.get_localzone() return timezone class POSIXct(POSIXt, FloatVector): """ Representation of dates as seconds since Epoch. This form is preferred to POSIXlt for inclusion in a DataFrame. POSIXlt(seq) -> POSIXlt. The constructor accepts either an R vector floats or a sequence (an object with the Python sequence interface) of time.struct_time objects. """ _as_posixct = baseenv_ri['as.POSIXct'] _ISOdatetime = baseenv_ri['ISOdatetime'] def __init__(self, seq): """ Create a POSIXct from either an R vector or a sequence of Python dates. """ if isinstance(seq, Sexp): init_param = seq elif isinstance(seq[0], struct_time): init_param = POSIXct.sexp_from_struct_time(seq) elif isinstance(seq[0], datetime): init_param = POSIXct.sexp_from_datetime(seq) else: raise TypeError( 'All elements must inherit from time.struct_time or ' 'datetime.datetime.') super().__init__(init_param) @staticmethod def _sexp_from_seq(seq, tz_info_getter, isodatetime_columns): """ return a POSIXct vector from a sequence of time.struct_time elements. """ tz_count = 0 tz_info = None for elt in conversion.noconversion(seq): tmp = tz_info_getter(elt) if tz_info is None: tz_info = tmp tz_count = 1 elif tz_info == tmp: tz_count += 1 else: # different time zones # TODO: create a list of time zones with tz_count times # tz_info, add the current tz_info and append further. raise ValueError( 'Sequences of dates with different time zones not ' 'yet allowed.' ) if tz_info is None: tz_info = tzname[0] # We could use R's as.POSIXct instead of ISOdatetime # since as.POSIXct is used by it anyway, but the overall # interface for dates and conversion between formats # is not exactly straightforward. Someone with more # time should look into this. d = isodatetime_columns(seq) sexp = POSIXct._ISOdatetime(*d, tz=StrSexpVector((tz_info, ))) return sexp @staticmethod def sexp_from_struct_time(seq): def f(seq): return [IntVector([x.tm_year for x in seq]), IntVector([x.tm_mon for x in seq]), IntVector([x.tm_mday for x in seq]), IntVector([x.tm_hour for x in seq]), IntVector([x.tm_min for x in seq]), IntVector([x.tm_sec for x in seq])] return POSIXct._sexp_from_seq(seq, lambda elt: time.tzname[0], f) @staticmethod def sexp_from_datetime(seq): """ return a POSIXct vector from a sequence of datetime.datetime elements. """ def f(seq): return [IntVector([x.year for x in seq]), IntVector([x.month for x in seq]), IntVector([x.day for x in seq]), IntVector([x.hour for x in seq]), IntVector([x.minute for x in seq]), IntVector([x.second for x in seq])] return POSIXct._sexp_from_seq(seq, attrgetter('tzinfo'), f) @staticmethod def isrinstance(obj) -> bool: """Is an R object an instance of POSIXct.""" return obj.rclass[0] == 'POSIXct' def iter_localized_datetime(self): """Iterator yielding localized Python datetime objects.""" try: r_tzone_name = self.do_slot('tzone')[0] except LookupError: warnings.warn('R object inheriting from "POSIXct" but without ' 'attribute "tzone".') r_tzone_name = '' if r_tzone_name == '': # R is implicitly using the local timezone, while Python # time libraries will assume UTC. r_tzone = get_timezone() else: r_tzone = pytz.timezone(r_tzone_name) for x in self: yield ( None if math.isnan(x) else datetime.fromtimestamp(x, r_tzone) ) class Array(Vector): """ An R array """ _dimnames_get = baseenv_ri['dimnames'] _dimnames_set = baseenv_ri['dimnames<-'] _dim_get = baseenv_ri['dim'] _dim_set = baseenv_ri['dim<-'] _isarray = baseenv_ri['is.array'] def __dim_get(self): res = self._dim_get(self) res = conversion.rpy2py(res) return res def __dim_set(self, value): # TODO: R will create a copy of the object upon assignment # of a new dimension attribute. raise NotImplementedError("Not yet implemented") value = conversion.py2rpy(value) self._dim_set(self, value) dim = property(__dim_get, __dim_set, "Get or set the dimension of the array.") def __dimnames_get(self): """ Return a list of name vectors (like the R function 'dimnames' does).""" res = self._dimnames_get(self) res = conversion.rpy2py(res) return res def __dimnames_set(self, value): """ Set list of name vectors (like the R function 'dimnames' does).""" value = conversion.rpy2py(value) res = self._dimnames_set(self, value) self.__sexp__ = res.__sexp__ names = property(__dimnames_get, __dimnames_set, None, "names associated with the dimension.") dimnames = names class IntArray(Array, IntVector): pass class ByteArray(Array, ByteVector): pass class FloatArray(Array, FloatVector): pass class BoolArray(Array, BoolVector): pass class ComplexArray(Array, ComplexVector): pass class StrArray(Array, StrVector): pass class Matrix(Array): """ An R matrix """ _transpose = baseenv_ri['t'] _rownames = baseenv_ri['rownames'] _colnames = baseenv_ri['colnames'] _dot = baseenv_ri['%*%'] _matmul = baseenv_ri['%*%'] _crossprod = baseenv_ri['crossprod'] _tcrossprod = baseenv_ri['tcrossprod'] _svd = baseenv_ri['svd'] _eigen = baseenv_ri['eigen'] def __nrow_get(self): """ Number of rows. :rtype: integer """ return self.dim[0] nrow = property(__nrow_get, None, None, "Number of rows") def __ncol_get(self): """ Number of columns. :rtype: integer """ return self.dim[1] ncol = property(__ncol_get, None, None, "Number of columns") def __rownames_get(self): """ Row names :rtype: SexpVector """ res = self._rownames(self) return conversion.rpy2py(res) def __rownames_set(self, rn): if isinstance(rn, StrSexpVector): if len(rn) != self.nrow: raise ValueError('Invalid length.') if self.dimnames is NULL: dn = ListVector.from_length(2) dn[0] = rn self.do_slot_assign('dimnames', dn) else: dn = self.dimnames dn[0] = rn else: raise ValueError( 'The rownames attribute can only be an R string vector.' ) rownames = property(__rownames_get, __rownames_set, None, "Row names") def __colnames_get(self): """ Column names :rtype: SexpVector """ res = self._colnames(self) return conversion.rpy2py(res) def __colnames_set(self, cn): if isinstance(cn, StrSexpVector): if len(cn) != self.ncol: raise ValueError('Invalid length.') if self.dimnames is NULL: dn = ListVector.from_length(2) dn[1] = cn self.do_slot_assign('dimnames', dn) else: dn = self.dimnames dn[1] = cn else: raise ValueError( 'The colnames attribute can only be an R string vector.' ) colnames = property(__colnames_get, __colnames_set, None, "Column names") def transpose(self): """ transpose the matrix """ res = self._transpose(self) return conversion.rpy2py(res) def __matmul__(self, x): """ Matrix multiplication. """ res = self._matmul(self, conversion.py2rpy(x)) return conversion.rpy2py(res) def crossprod(self, m): """ crossproduct X'.Y""" res = self._crossprod(self, conversion.rpy2py(m)) return conversion.rpy2py(res) def tcrossprod(self, m): """ crossproduct X.Y'""" res = self._tcrossprod(self, m) return conversion.rpy2py(res) def svd(self, nu=None, nv=None, linpack=False): """ SVD decomposition. If nu is None, it is given the default value min(tuple(self.dim)). If nv is None, it is given the default value min(tuple(self.dim)). """ if nu is None: nu = min(tuple(self.dim)) if nv is None: nv = min(tuple(self.dim)) res = self._svd(self, nu=nu, nv=nv, LINPACK=False) return conversion.rpy2py(res) def dot(self, m): """ Matrix multiplication """ res = self._dot(self, m) return conversion.rpy2py(res) def eigen(self): """ Eigen values """ res = self._eigen(self) return conversion.rpy2py(res) class DataFrame(ListVector): """ R 'data.frame'. """ _dataframe_name = rinterface.StrSexpVector(('data.frame',)) _read_csv = utils_ri['read.csv'] _write_table = utils_ri['write.table'] _cbind = rinterface.baseenv['cbind.data.frame'] _rbind = rinterface.baseenv['rbind.data.frame'] _is_list = rinterface.baseenv['is.list'] _html_template = jinja2.Template( """ R/rpy2 DataFrame ({{ nrows }} x {{ ncolumns }}) {% for name in column_names %} {% endfor %} {% for row_i in rows %} {% for col_i in columns %} {% endfor %} {% endfor %}
{{ name }}
{{ elements[col_i][row_i] }}
""") def __init__(self, obj, stringsasfactor=False): """ Create a new data frame. :param obj: object inheriting from rpy2.rinterface.SexpVector, or inheriting from TaggedList or a mapping name -> value :param stringsasfactors: Boolean indicating whether vectors of strings should be turned to vectors. Note that factors will not be turned to string vectors. """ if isinstance(obj, rinterface.ListSexpVector): if obj.typeof != rinterface.RTYPES.VECSXP: raise ValueError( "obj should of typeof RTYPES.VECSXP " " (and we get %s)" % rinterface.RTYPES(obj.typeof) ) if ( self._is_list(obj)[0] or globalenv_ri.find('inherits')( obj, self._dataframe_name )[0] ): # TODO: is it really a good idea to pass R lists # to the constructor ? super().__init__(obj) return else: raise ValueError( "When passing R objects to build a DataFrame, " "the R object must be a list or inherit from " "the R class 'data.frame'." ) elif isinstance(obj, rlc.TaggedList): kv = [(k, conversion.py2rpy(v)) for k, v in obj.items()] else: try: kv = [(str(k), conversion.py2rpy(v)) for k, v in obj.items()] except AttributeError: raise ValueError( 'obj can only be' 'an instance of rpy2.rinterface.ListSexpVector, ' 'an instance of TaggedList, ' 'or an objects with a methods items() that returns ' '(key, value) pairs ' '(such a Python dict, rpy2.rlike.container OrdDict).') # Check if there is a conflicting column name if 'stringsAsFactors' in (k for k, v in kv): warnings.warn('The column name "stringsAsFactors" is ' 'conflicting with named parameter ' 'in underlying R function "data.frame()".') else: kv.append(('stringsAsFactors', stringsasfactor)) # Call R's data frame constructor kv = tuple(kv) df = baseenv_ri.find("data.frame").rcall(kv, globalenv_ri) super().__init__(df) def _repr_html_(self, max_items=7): names = list() if len(self) <= max_items: names.extend(self.names) else: half_items = max_items // 2 for i in range(0, half_items): try: name = self.names[i] except TypeError: name = '[no name]' names.append(name) names.append('...') for i in range(-half_items, 0): try: name = self.names[i] except TypeError: name = '[no name]' names.append(name) elements = list() for e in self._iter_repr(max_items=max_items): if hasattr(e, '_repr_html_'): elements.append(tuple(e._iter_formatted())) else: elements.append(['...', ]) d = {'column_names': names, 'rows': range(len(elements[0]) if len(elements) else 0), 'columns': tuple(range(len(names))), 'nrows': self.nrow, 'ncolumns': self.ncol, 'elements': elements} html = self._html_template.render(d) return html def _get_nrow(self): """ Number of rows. :rtype: integer """ return baseenv_ri["nrow"](self)[0] nrow = property(_get_nrow, None, None) def _get_ncol(self): """ Number of columns. :rtype: integer """ return baseenv_ri["ncol"](self)[0] ncol = property(_get_ncol, None, None) def _get_rownames(self): res = baseenv_ri["rownames"](self) return conversion.rpy2py(res) def _set_rownames(self, rownames): res = baseenv_ri["rownames<-"](self, conversion.py2rpy(rownames)) self.__sexp__ = res.__sexp__ rownames = property(_get_rownames, _set_rownames, None, 'Row names') def _get_colnames(self): res = baseenv_ri["colnames"](self) return conversion.rpy2py(res) def _set_colnames(self, colnames): res = baseenv_ri["colnames<-"](self, conversion.py2rpy(colnames)) self.__sexp__ = res.__sexp__ colnames = property(_get_colnames, _set_colnames, None) def __getitem__(self, i): # Make sure this is not a List returned # 3rd-party conversions could return objects # that no longer inherit from rpy2's R objects. # We need to use the low-level __getitem__ # to bypass the conversion mechanism. # R's data.frames have no representation at the C-API level # (they are lists) tmp = rinterface.ListSexpVector.__getitem__(self, i) if tmp.typeof == rinterface.RTYPES.VECSXP: return DataFrame(tmp) else: return conversion.rpy2py(tmp) def cbind(self, *args, **kwargs): """ bind objects as supplementary columns """ new_args = [self, ] + [conversion.rpy2py(x) for x in args] new_kwargs = dict( [(k, conversion.rpy2py(v)) for k, v in kwargs.items()] ) res = self._cbind(*new_args, **new_kwargs) return conversion.rpy2py(res) def rbind(self, *args, **kwargs): """ bind objects as supplementary rows """ new_args = [conversion.rpy2py(x) for x in args] new_kwargs = dict( [(k, conversion.rpy2py(v)) for k, v in kwargs.items()] ) res = self._rbind(self, *new_args, **new_kwargs) return conversion.rpy2py(res) def head(self, *args, **kwargs): """ Call the R generic 'head()'. """ res = utils_ri['head'](self, *args, **kwargs) return conversion.rpy2py(res) @staticmethod def from_csvfile(path, header=True, sep=',', quote='"', dec='.', row_names=rinterface.MissingArg, col_names=rinterface.MissingArg, fill=True, comment_char='', na_strings=[], as_is=False): """ Create an instance from data in a .csv file. :param path: string with a path :param header: boolean (heading line with column names or not) :param sep: separator character :param quote: quote character :param row_names: column name, or column index for column names (warning: indexing starts at one in R) :param fill: boolean (fill the lines when less entries than columns) :param comment_char: comment character :param na_strings: a list of strings which are interpreted to be NA values :param as_is: boolean (keep the columns of strings as such, or turn them into factors) """ path = conversion.py2rpy(path) header = conversion.py2rpy(header) sep = conversion.py2rpy(sep) quote = conversion.py2rpy(quote) dec = conversion.py2rpy(dec) if row_names is not rinterface.MissingArg: row_names = conversion.py2rpy(row_names) if col_names is not rinterface.MissingArg: col_names = conversion.py2rpy(col_names) fill = conversion.py2rpy(fill) comment_char = conversion.py2rpy(comment_char) as_is = conversion.py2rpy(as_is) na_strings = conversion.py2rpy(na_strings) res = DataFrame._read_csv(path, **{'header': header, 'sep': sep, 'quote': quote, 'dec': dec, 'row.names': row_names, 'col.names': col_names, 'fill': fill, 'comment.char': comment_char, 'na.strings': na_strings, 'as.is': as_is}) res = conversion.rpy2py(res) return res def to_csvfile(self, path, quote=True, sep=',', eol=os.linesep, na='NA', dec='.', row_names=True, col_names=True, qmethod='escape', append=False): """ Save the data into a .csv file. path : string with a path quote : quote character sep : separator character eol : end-of-line character(s) na : string for missing values dec : string for decimal separator row_names : boolean (save row names, or not) col_names : boolean (save column names, or not) comment_char : method to 'escape' special characters append : boolean (append if the file in the path is already existing, or not) """ path = conversion.py2rpy(path) append = conversion.py2rpy(append) sep = conversion.py2rpy(sep) eol = conversion.py2rpy(eol) na = conversion.py2rpy(na) dec = conversion.py2rpy(dec) row_names = conversion.py2rpy(row_names) col_names = conversion.py2rpy(col_names) qmethod = conversion.py2rpy(qmethod) res = self._write_table( self, **{'file': path, 'quote': quote, 'sep': sep, 'eol': eol, 'na': na, 'dec': dec, 'row.names': row_names, 'col.names': col_names, 'qmethod': qmethod, 'append': append}) return res def iter_row(self): """ iterator across rows """ for i in range(self.nrow): yield self.rx(i+1, rinterface.MissingArg) def iter_column(self): """ iterator across columns """ for i in range(self.ncol): yield self.rx(rinterface.MissingArg, i+1) class IntMatrix(Matrix, IntVector): pass class ByteMatrix(Matrix, ByteVector): pass class FloatMatrix(Matrix, FloatVector): pass class BoolMatrix(Matrix, BoolVector): pass class ComplexMatrix(Matrix, ComplexVector): pass class StrMatrix(Matrix, StrVector): pass rtypeof2rotype = { rinterface.RTYPES.INTSXP: IntVector, rinterface.RTYPES.REALSXP: FloatVector, rinterface.RTYPES.STRSXP: StrVector, rinterface.RTYPES.CPLXSXP: ComplexVector, rinterface.RTYPES.LGLSXP: BoolVector } __all__ = ['Vector', 'StrVector', 'IntVector', 'BoolVector', 'ComplexVector', 'FloatVector', 'FactorVector', 'Vector', 'ListVector', 'POSIXlt', 'POSIXct', 'Array', 'Matrix', 'DataFrame'] rpy2-3.2.6/rpy2/robjects/environments.py0000644000175000017500000000744713615570032021412 0ustar laurentlaurent00000000000000import typing import rpy2.rinterface as rinterface from rpy2.robjects.robject import RObjectMixin from rpy2.robjects import conversion _new_env = rinterface.baseenv["new.env"] class Environment(RObjectMixin, rinterface.SexpEnvironment): """ An R environement, implementing Python's mapping interface. """ def __init__(self, o=None): if o is None: o = _new_env(hash=rinterface.BoolSexpVector([True, ])) super(Environment, self).__init__(o) def __getitem__(self, item): res = super(Environment, self).__getitem__(item) res = conversion.converter.rpy2py(res) # objects in a R environment have an associated name / symbol try: res.__rname__ = item except AttributeError: # the 3rd-party conversion function can return objects # for which __rname__ cannot be set (because of fixed # __slots__ and no __rname__ in the original set # of attributes) pass return res def __setitem__(self, item, value) -> None: robj = conversion.converter.py2rpy(value) super(Environment, self).__setitem__(item, robj) @property def enclos(self): return conversion.converter.rpy2py(super().enclos) @property def frame(self): return conversion.converter.rpy2py(super().frame) def find(self, item, wantfun=False): """Find an item, starting with this R environment. Raises a `KeyError` if the key cannot be found. This method is called `find` because it is somewhat different from the method :meth:`get` in Python mappings such :class:`dict`. This is looking for a key across enclosing environments, returning the first key found. :param item: string (name/symbol) :rtype: object (as returned by :func:`conversion.converter.rpy2py`) """ res = super(Environment, self).find(item, wantfun=wantfun) res = conversion.converter.rpy2py(res) # TODO: There is a design issue here. The attribute __rname__ is # intended to store the symbol name of the R object but this is # meaningless for non-rpy2 objects. try: res.__rname__ = item except AttributeError: pass return res def keys(self) -> typing.Generator[str, None, None]: """ Return an iterator over keys in the environment.""" return super().keys() def items(self) -> typing.Generator: """ Iterate through the symbols and associated objects in this R environment.""" for k in self: yield (k, self[k]) def values(self) -> typing.Generator: """ Iterate through the objects in this R environment.""" for k in self: yield self[k] def pop(self, k, *args): """ E.pop(k[, d]) -> v, remove the specified key and return the corresponding value. If the key is not found, d is returned if given, otherwise KeyError is raised.""" if k in self: v = self[k] del(self[k]) elif args: if len(args) > 1: raise ValueError('Invalid number of optional parameters.') v = args[0] else: raise KeyError(k) return v def popitem(self): """ E.popitem() -> (k, v), remove and return some (key, value) pair as a 2-tuple; but raise KeyError if E is empty. """ if len(self) == 0: raise KeyError() kv = next(self.items()) del(self[kv[0]]) return kv def clear(self) -> None: """ E.clear() -> None. Remove all items from D. """ # FIXME: is there a more efficient implementation (when large # number of keys) ? for k in self: del(self[k]) rpy2-3.2.6/rpy2/robjects/robject.py0000644000175000017500000001205113615570032020276 0ustar laurentlaurent00000000000000import os import sys import tempfile import weakref import rpy2.rinterface import rpy2.rinterface_lib.callbacks from . import conversion rpy2.rinterface.initr() class RSlots(object): """ Attributes of an R object as a Python mapping. The parent proxy to the underlying R object is held as a weak reference. The attributes are therefore not protected from garbage collection unless bound to a Python symbol or in an other container. """ __slots__ = ['_robj', ] def __init__(self, robj): self._robj = weakref.proxy(robj) def __getitem__(self, key): value = self._robj.do_slot(key) return conversion.rpy2py(value) def __setitem__(self, key, value): rpy2_value = conversion.py2rpy(value) self._robj.do_slot_assign(key, rpy2_value) def __len__(self): return len(self._robj.list_attrs()) def keys(self): for k in self._robj.list_attrs(): yield k __iter__ = keys def items(self): for k in self._robj.list_attrs(): v = self[k] yield (k, v) def values(self): for k in self._robj.list_attrs(): v = self[k] yield v _get_exported_value = rpy2.rinterface.baseenv['::'] class RObjectMixin(object): """ Class to provide methods common to all RObject instances. """ __rname__ = None __tempfile = rpy2.rinterface.baseenv.find("tempfile") __file = rpy2.rinterface.baseenv.find("file") __fifo = rpy2.rinterface.baseenv.find("fifo") __sink = rpy2.rinterface.baseenv.find("sink") __close = rpy2.rinterface.baseenv.find("close") __readlines = rpy2.rinterface.baseenv.find("readLines") __unlink = rpy2.rinterface.baseenv.find("unlink") __show = _get_exported_value('methods', 'show') __slots = None @property def slots(self): """ Attributes of the underlying R object as a Python mapping. The attributes can accessed and assigned by name (as if they were in a Python `dict`).""" if self.__slots is None: self.__slots = RSlots(self) return self.__slots def __repr__(self): try: rclasses = ('R object with classes: {} mapped to:' .format(tuple(self.rclass))) except Exception: rclasses = 'Unable to fetch R classes.' + os.linesep os.linesep.join((rclasses, repr(super()))) return rclasses def __str__(self): s = [] with (rpy2.rinterface_lib .callbacks.obj_in_module(rpy2.rinterface_lib.callbacks, 'consolewrite_print', s.append)): self.__show(self) s = str.join('', s) return s def __getstate__(self, ): return (super().__getstate__(), self.__dict__.copy()) def __setstate__(self, state): rds, __dict__ = state super().__setstate__(rds) self.__dict__.update(__dict__) def r_repr(self): """ String representation for an object that can be directly evaluated as R code. """ return repr_robject(self, linesep='\n') @property def rclass(self): """ R class for the object, stored as an R string vector. When setting the rclass, the new value will be: - wrapped in a Python tuple if a string (the R class is a vector of strings, and this is made for convenience) - wrapped in a StrSexpVector Note that when setting the class R may make a copy of the whole object (R is mostly a functional language). If this must be avoided, and if the number of parent classes before and after the change are compatible, the class name can be changed in-place by replacing vector elements.""" try: res = super(RObjectMixin, self).rclass res = rpy2.rinterface.sexp.rclass_get(self.__sexp__) return res except rpy2.rinterface._rinterface.embedded.RRuntimeError as rre: if self.typeof == rpy2.rinterface.RTYPES.SYMSXP: # Unevaluated expression: has no class. return (None, ) else: raise rre @rclass.setter def rclass(self, value): if isinstance(value, str): value = (value, ) new_cls = rpy2.rinterface.StrSexpVector(value) rpy2.rinterface.sexp.rclass_set(self.__sexp__, new_cls) def repr_robject(o, linesep=os.linesep): s = rpy2.rinterface.baseenv.find("deparse")(o) s = str.join(linesep, s) return s class RObject(RObjectMixin, rpy2.rinterface.Sexp): """ Base class for all non-vector R objects. """ def __setattr__(self, name, value): if name == '_sexp': if not isinstance(value, rpy2.rinterface.Sexp): raise ValueError( '_attr must contain an object ' 'that inherits from rpy2.rinterface.Sexp ' '(not from %s)' % type(value) ) super(RObject, self).__setattr__(name, value) rpy2-3.2.6/rpy2/__init__.py0000644000175000017500000000013213615570032016567 0ustar laurentlaurent00000000000000 __version_vector__ = (3,2,6) __version__ = '.'.join(str(x) for x in __version_vector__) rpy2-3.2.6/rpy2/ipython/0000755000175000017500000000000013615572523016163 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/ipython/html.py0000644000175000017500000002167313576515767017527 0ustar laurentlaurent00000000000000import jinja2 from rpy2.robjects import (vectors, RObject, SignatureTranslatedFunction, RS4) from rpy2 import rinterface from rpy2.robjects.packages import SourceCode from rpy2.robjects.packages import wherefrom from IPython import get_ipython template_list = jinja2.Template("""

{{ clsname }} with {{ rlist | length }} elements:

{%- for elt_i in range(display_neltmax) %}
{{ rlist.names[elt_i] }}
{{ rlist[elt_i] }}
{%- endfor %} {%- if display_neltmax < (rlist | length) %}
...
{%- endif %}
""") template_vector_horizontal = jinja2.Template(""" {{ clsname }} with {{ vector | length }} elements: {%- for elt_i in range(display_ncolmax - size_tail) %} {%- endfor %} {%- if display_ncolmax < (vector | length) %} {%- endif %} {%- for elt_i in elt_i_tail %} {%- endfor %}
{{ vector[elt_i] }}...{{ vector[elt_i] }}
""") template_vector_vertical = jinja2.Template(""" {{ clsname }} with {{ vector | length }} elements: {%- for elt_i in range(display_nrowmax - size_tail) %} {%- if has_vector_names %} {%- endif %} {%- endfor %} {%- if display_nrowmax < (vector | length) %} {%- if has_vector_names %} {%- endif %} {%- endif %} {%- for elt_i in elt_i_tail %} {%- if has_vector_names %} {%- endif %} {%- endfor %}
{{ elt_i }}{{ vector.names[elt_i] }}{{ "%s" | format(vector[elt_i]) | truncate(12) }}
.........
{{ elt_i }}{{ vector.names[elt_i] }}{{ "%s" | format(vector[elt_i]) | truncate(12) }}
""") template_dataframe = jinja2.Template(""" {{ clsname }} with {{ dataf.nrow }} rows and {{ dataf | length }} columns: {%- if has_rownames %} {%- endif %} {%- for col_i in range(display_ncolmax - size_coltail) %} {%- endfor %} {%- if display_ncolmax < dataf.ncol %} {%- endif %} {%- for col_i in col_i_tail %} {%- endfor %} {%- for row_i in range(display_nrowmax - size_rowtail) %} {%- if has_rownames %} {%- endif %} {%- for col_i in range(display_ncolmax - size_coltail) %} {%- endfor %} {%- if display_ncolmax < dataf.ncol %} {%- endif %} {%- for col_i in col_i_tail %} {%- endfor %} {%- endfor %} {%- if dataf.nrow > display_nrowmax %} {%- if has_rownames %} {%- endif %} {%- for col_i in range(display_ncolmax - size_coltail) %} {%- endfor %} {%- if display_ncolmax < dataf.ncol %} {%- endif %} {%- for col_i in range(2) %} {%- endfor %} {%- endif %} {%- for row_i in row_i_tail %} {%- if has_rownames %} {%- endif %} {%- for col_i in range(display_ncolmax - size_coltail) %} {%- endfor %} {%- if display_ncolmax < dataf.ncol %} {%- endif %} {%- for col_i in col_i_tail %} {%- endfor %} {%- endfor %}
{{ dataf.names[col_i] }}...{{ dataf.names[col_i] }}
{{ row_i }}{{ dataf.rownames[row_i] }}{{ dataf[col_i][row_i] }}...{{ dataf[col_i][row_i] }}
...............
{{ row_i }}{{ dataf.rownames[row_i] }}{{ dataf[col_i][row_i] }}...{{ dataf[col_i][row_i] }}
""") template_ridentifiedobject = jinja2.Template("""
  • {{ clsname }} object
  • Origin in R: {{ origin }}
  • Class(es) in R:
      {%- for rclsname in obj.rclass %}
    • {{ rclsname }}
    • {%- endfor %}
""") template_rs4 = jinja2.Template("""
{{ clsname }} object
Origin in R: {{ origin }}
Class(es) in R:
    {%- for rclsname in obj.rclass %}
  • {{ rclsname }}
  • {%- endfor %}
Attributes:
    {%- for sln in obj.slotnames() %}
  • {{ sln }}
  • {%- endfor %}
""") template_sourcecode = jinja2.Template("""

R source code:

{{ sourcecode }} """) class StrFactorVector(vectors.FactorVector): def __getitem__(self, item): integer = super(StrFactorVector, self).__getitem__(item) # R is one-offset, Python is zero-offset return self.levels[integer-1] class StrDataFrame(vectors.DataFrame): def __getitem__(self, item): obj = super(StrDataFrame, self).__getitem__(item) if isinstance(obj, vectors.FactorVector): obj = StrFactorVector(obj) return obj def html_vector_horizontal(vector, display_ncolmax=10, size_tail=2, table_class='rpy2_table'): if isinstance(vector, vectors.FactorVector): vector = StrFactorVector(vector) html = template_vector_horizontal.render({ 'table_class': table_class, 'clsname': type(vector).__name__, 'vector': vector, 'display_ncolmax': min(display_ncolmax, len(vector)), 'size_tail': size_tail, 'elt_i_tail': range(max(0, len(vector)-size_tail), len(vector))}) return html def html_rlist(vector, display_nrowmax=10, size_tail=2, table_class='rpy2_table'): rg = range(max(0, len(vector)-size_tail), len(vector)) html = template_vector_vertical.render({ 'table_class': table_class, 'clsname': type(vector).__name__, 'vector': vector, 'has_vector_names': vector.names is not rinterface.NULL, 'display_nrowmax': min(display_nrowmax, len(vector)), 'size_tail': size_tail, 'elt_i_tail': rg}) return html def html_rdataframe(dataf, display_nrowmax=10, display_ncolmax=6, size_coltail=2, size_rowtail=2, table_class='rpy2_table'): html = template_dataframe.render( {'dataf': StrDataFrame(dataf), 'table_class': table_class, 'has_rownames': dataf.rownames is not None, 'clsname': type(dataf).__name__, 'display_nrowmax': min(display_nrowmax, dataf.nrow), 'display_ncolmax': min(display_ncolmax, dataf.ncol), 'col_i_tail': range(max(0, dataf.ncol-size_coltail), dataf.ncol), 'row_i_tail': range(max(0, dataf.nrow-size_rowtail), dataf.nrow), 'size_coltail': size_coltail, 'size_rowtail': size_rowtail}) return html def html_sourcecode(sourcecode): from pygments import highlight from pygments.lexers import SLexer from pygments.formatters import HtmlFormatter formatter = HtmlFormatter() htmlcode = highlight(sourcecode, SLexer(), formatter) d = {'sourcecode': htmlcode, 'syntax_highlighting': formatter.get_style_defs()} html = template_sourcecode.render(d) return html def _dict_ridentifiedobject(obj): if hasattr(obj, '__rname__') and obj.__rname__ is not None: env = wherefrom(obj.__rname__) try: origin = env.do_slot('name')[0] except LookupError: origin = 'package:base ?' else: origin = '???' d = {'clsname': type(obj).__name__, 'origin': origin, 'obj': obj} return d def html_ridentifiedobject(obj): d = _dict_ridentifiedobject(obj) html = template_ridentifiedobject.render(d) return html def html_rs4(obj, table_class='rpy2_table'): d = _dict_ridentifiedobject(obj) d['table_class'] = table_class html = template_rs4.render(d) return html def init_printing(): ip = get_ipython() html_f = ip.display_formatter.formatters['text/html'] html_f.for_type(vectors.Vector, html_vector_horizontal) html_f.for_type(vectors.ListVector, html_rlist) html_f.for_type(vectors.DataFrame, html_rdataframe) html_f.for_type(RObject, html_ridentifiedobject) html_f.for_type(RS4, html_rs4) html_f.for_type(SignatureTranslatedFunction, html_ridentifiedobject) html_f.for_type(SourceCode, html_sourcecode) rpy2-3.2.6/rpy2/ipython/__init__.py0000644000175000017500000000011513576515767020306 0ustar laurentlaurent00000000000000from . import rmagic load_ipython_extension = rmagic.load_ipython_extension rpy2-3.2.6/rpy2/ipython/ggplot.py0000644000175000017500000000463613576515767020057 0ustar laurentlaurent00000000000000""" Utilities for using ggplot2 witn ipython / jupyter. """ from rpy2 import robjects from rpy2.robjects.lib import ggplot2, grdevices from IPython import get_ipython from IPython.core.display import Image class GGPlot(ggplot2.GGPlot): def png(self, width=700, height=500): """ Build an Ipython "Image" (requires iPython). """ return image_png(self, width=width, height=height) class GGPlotSVG(ggplot2.GGPlot): """ The embedding of several SVG figures into one ipython notebook is giving garbled figures. The SVG functionality is taken out to a child class. """ def svg(self, width=6, height=4): """ Build an Ipython "Image" (requires iPython). """ with grdevices.render_to_bytesio(grdevices.svg, width=width, height=height) as b: robjects.r("print")(self) data = b.getvalue() ip_img = Image(data=data, format='svg', embed=False) return ip_img def image_png(gg, width=800, height=400): with grdevices.render_to_bytesio(grdevices.png, type="cairo-png", width=width, height=height, antialias="subpixel") as b: robjects.r("print")(gg) data = b.getvalue() ip_img = Image(data=data, format='png', embed=True) return ip_img def display_png(gg, width=800, height=400): ip_img = image_png(gg, width=width, height=height) return ip_img._repr_png_() def set_png_formatter(): # register display func with PNG formatter: png_formatter = get_ipython().display_formatter.formatters['image/png'] dpi = png_formatter.for_type(ggplot2.GGPlot, display_png) return dpi class PNGplot(object): """ Context manager """ def __init__(self, width=600, height=400): self._width = width self._height = height png_formatter = get_ipython().display_formatter.formatters['image/png'] self._png_formatter = png_formatter self._for_ggplot = self._png_formatter.for_type(ggplot2.GGPlot) def __enter__(self): self._png_formatter.for_type(ggplot2.GGPlot, display_png) return None def __exit__(self, exc_type, exc_val, exc_tb): self._png_formatter.for_type(ggplot2.GGPlot, self._for_ggplot) return False rpy2-3.2.6/rpy2/ipython/rmagic.py0000644000175000017500000006562013576515767020025 0ustar laurentlaurent00000000000000# -*- coding: utf-8 -*- """ ====== Rmagic ====== Magic command interface for interactive work with R in ipython. %R and %%R are the line and cell magics, respectively. .. note:: You will need a working copy of R. Usage ===== To enable the magics below, execute `%load_ext rpy2.ipython`. `%R` {R_DOC} `%Rpush` {RPUSH_DOC} `%Rpull` {RPULL_DOC} `%Rget` {RGET_DOC} """ # ----------------------------------------------------------------------------- # Copyright (C) 2012 The IPython Development Team # Copyright (C) 2013-2019 rpy2 authors # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. # ----------------------------------------------------------------------------- import contextlib import sys import tempfile from glob import glob from os import stat from shutil import rmtree import textwrap # numpy and rpy2 imports import rpy2.rinterface as ri import rpy2.rinterface_lib.callbacks import rpy2.robjects as ro import rpy2.robjects.packages as rpacks from rpy2.robjects.lib import grdevices from rpy2.robjects.conversion import (Converter, localconverter) import warnings from rpy2.robjects.conversion import converter as template_converter # Try loading pandas and numpy, emitting a warning if either cannot be # loaded. try: import numpy try: import pandas except ImportError as ie: pandas = None warnings.warn('The Python package `pandas` is strongly ' 'recommended when using `rpy2.ipython`. ' 'Unfortunately it could not be loaded ' '(error: %s), ' 'but at least we found `numpy`.' % str(ie)) except ImportError as ie: # Give up on numerics numpy = None warnings.warn('The Python package `pandas` is strongly ' 'recommended when using `rpy2.ipython`. ' 'Unfortunately it could not be loaded, ' 'as we did not manage to load `numpy` ' 'in the first place (error: %s).' % str(ie)) # IPython imports. from IPython.core import displaypub from IPython.core.magic import (Magics, magics_class, line_cell_magic, line_magic, needs_local_scope) from IPython.core.magic_arguments import (argument, argument_group, magic_arguments, parse_argstring) if numpy: from rpy2.robjects import numpy2ri template_converter += numpy2ri.converter if pandas: from rpy2.robjects import pandas2ri template_converter += pandas2ri.converter class RInterpreterError(ri.embedded.RRuntimeError): """An error when running R code in a %%R magic cell.""" msg_prefix_template = ('Failed to parse and evaluate line %r.\n' 'R error message: %r') rstdout_prefix = '\nR stdout:\n' def __init__(self, line, err, stdout): self.line = line self.err = err.rstrip() self.stdout = stdout.rstrip() def __str__(self): s = (self.msg_prefix_template % (self.line, self.err)) if self.stdout and (self.stdout != self.err): s += self.rstdout_prefix + self.stdout return s converter = Converter('ipython conversion', template=template_converter) # The default conversion for lists is currently to make them an R list. That # has some advantages, but can be inconvenient (and, it's inconsistent with # the way python lists are automatically converted by numpy functions), so # for interactive use in the rmagic, we call unlist, which converts lists to # vectors **if the list was of uniform (atomic) type**. @converter.rpy2py.register(list) def rpy2py_list(obj): # simplify2array is a utility function, but nice for us # TODO: use an early binding of the R function return ro.r.simplify2array(obj) # TODO: remove ? # # The R magic is opiniated about what the R vectors should become. # @converter.ri2ro.register(ri.SexpVector) # def _(obj): # if 'data.frame' in obj.rclass: # # request to turn it to a pandas DataFrame # res = converter.rpy2py(obj) # else: # res = ro.sexpvector_to_ro(obj) # return res @magics_class class RMagics(Magics): """A set of magics useful for interactive work with R via rpy2. """ def __init__(self, shell, converter=converter, cache_display_data=False, device='png'): """ Parameters ---------- shell : IPython shell converter : rpy2 Converter instance to use. If None, the magic's current converter is used. cache_display_data : bool If True, the published results of the final call to R are cached in the variable 'display_cache'. device : ['png', 'X11', 'svg'] Device to be used for plotting. Currently only 'png', 'X11' and 'svg' are supported, with 'png' and 'svg' being most useful in the notebook, and 'X11' allowing interactive plots in the terminal. """ super(RMagics, self).__init__(shell) self.cache_display_data = cache_display_data self.Rstdout_cache = [] self.converter = converter self.set_R_plotting_device(device) def set_R_plotting_device(self, device): """ Set which device R should use to produce plots. If device == 'svg' then the package 'Cairo' must be installed. Because Cairo forces "onefile=TRUE", it is not posible to include multiple plots per cell. Parameters ---------- device : ['png', 'X11', 'svg'] Device to be used for plotting. Currently only "png" and "X11" are supported, with 'png' and 'svg' being most useful in the notebook, and 'X11' allowing interactive plots in the terminal. """ device = device.strip() if device not in ['png', 'X11', 'svg']: raise ValueError( "device must be one of ['png', 'X11' 'svg'], got '%s'", device) if device == 'svg': try: self.cairo = rpacks.importr('Cairo') except ri.embedded.RRuntimeError as rre: if rpacks.isinstalled('Cairo'): msg = ('An error occurred when trying to load the ' + 'R package Cairo\'\n%s' % str(rre)) else: msg = textwrap.dedent(""" The R package 'Cairo' is required but it does not appear to be installed/available. Try: import rpy2.robjects.packages as rpacks utils = rpacks.importr('utils') utils.chooseCRANmirror(ind=1) utils.install_packages('Cairo') """) raise RInterpreterError(msg) self.device = device @line_magic def Rdevice(self, line): """ Change the plotting device R uses to one of ['png', 'X11', 'svg']. """ self.set_R_plotting_device(line.strip()) def eval(self, code): """ Parse and evaluate a line of R code with rpy2. Returns the output to R's stdout() connection, the value generated by evaluating the code, and a boolean indicating whether the return value would be visible if the line of code were evaluated in an R REPL. R Code evaluation and visibility determination are done via an R call of the form withVisible(code_string), and this entire expression needs to be evaluated in R (we can't use rpy2 function proxies here, as withVisible is a LISPy R function). """ with contextlib.ExitStack() as stack: if self.cache_display_data: stack.enter( rpy2.rinterface_lib .callbacks.obj_in_module(rpy2.rinterface_lib.callbacks, 'consolewrite_print', self.write_console_regular) ) try: # Need the newline in case the last line in code is a comment. value, visible = ro.r("withVisible({%s\n})" % code) except (ri.embedded.RRuntimeError, ValueError) as exception: # Otherwise next return seems to have copy of error. warning_or_other_msg = self.flush() raise RInterpreterError(code, str(exception), warning_or_other_msg) text_output = self.flush() return text_output, value, visible[0] def write_console_regular(self, output): """ A hook to capture R's stdout in a cache. """ self.Rstdout_cache.append(output) def flush(self): """ Flush R's stdout cache to a string, returning the string. """ value = ''.join(self.Rstdout_cache) self.Rstdout_cache = [] return value # @skip_doctest @needs_local_scope @line_magic def Rpush(self, line, local_ns=None): """ A line-level magic for R that pushes variables from python to rpy2. The line should be made up of whitespace separated variable names in the IPython namespace:: In [7]: import numpy as np In [8]: X = np.array([4.5,6.3,7.9]) In [9]: X.mean() Out[9]: 6.2333333333333343 In [10]: %Rpush X In [11]: %R mean(X) Out[11]: array([ 6.23333333]) """ if local_ns is None: local_ns = {} inputs = line.split(' ') for input in inputs: try: val = local_ns[input] except KeyError: try: val = self.shell.user_ns[input] except KeyError: # reraise the KeyError as a NameError so that it looks like # the standard python behavior when you use an unnamed # variable raise NameError("name '%s' is not defined" % input) with localconverter(self.converter): ro.r.assign(input, val) # @skip_doctest @magic_arguments() @argument( 'outputs', nargs='*', ) @line_magic def Rpull(self, line): """ A line-level magic for R that pulls variables from python to rpy2:: In [18]: _ = %R x = c(3,4,6.7); y = c(4,6,7); z = c('a',3,4) In [19]: %Rpull x y z In [20]: x Out[20]: array([ 3. , 4. , 6.7]) In [21]: y Out[21]: array([ 4., 6., 7.]) In [22]: z Out[22]: array(['a', '3', '4'], dtype='|S1') This is useful when a structured array is desired as output, or when the object in R has mixed data types. See the %%R docstring for more examples. Notes ----- Beware that R names can have dots ('.') so this is not fool proof. To avoid this, don't name your R objects with dots... """ args = parse_argstring(self.Rpull, line) outputs = args.outputs with localconverter(self.converter): for output in outputs: robj = ri.globalenv.find(output) self.shell.push({output: robj}) # @skip_doctest @magic_arguments() @argument( 'output', nargs=1, type=str, ) @line_magic def Rget(self, line): """ Return an object from rpy2, possibly as a structured array (if possible). Similar to Rpull except only one argument is accepted and the value is returned rather than pushed to self.shell.user_ns:: In [3]: dtype=[('x', '= 1000: with open(imgfile, 'rb') as fh_img: images.append(fh_img.read()) else: # as onefile=TRUE, there is only one .svg file imgfile = "%s/Rplot.svg" % graph_dir # Cairo creates an SVG file every time R is called # -- empty ones are not published if stat(imgfile).st_size >= 1000: with open(imgfile, 'rb') as fh_img: images.append(fh_img.read()) mimetypes = {'png': 'image/png', 'svg': 'image/svg+xml'} mime = mimetypes[self.device] # By default, isolate SVG images in the Notebook to avoid garbling. if images and self.device == "svg" and isolate_svgs: md = {'image/svg+xml': dict(isolated=True)} # Flush text streams before sending figures, helps a little with # output. for image in images: # Synchronization in the console (though it's a bandaid, not a # real solution). sys.stdout.flush() sys.stderr.flush() display_data.append(('RMagic.R', {mime: image})) return display_data, md # @skip_doctest @magic_arguments() @argument( '-i', '--input', action='append', help=textwrap.dedent(""" Names of input variable from `shell.user_ns` to be assigned to R variables of the same names after using the Converter self.converter. Multiple names can be passed separated only by commas with no whitespace.""") ) @argument( '-o', '--output', action='append', help=textwrap.dedent(""" Names of variables to be pushed from rpy2 to `shell.user_ns` after executing cell body (rpy2's internal facilities will apply ri2ro as appropriate). Multiple names can be passed separated only by commas with no whitespace.""") ) @argument( '-n', '--noreturn', help='Force the magic to not return anything.', action='store_true', default=False ) @argument_group("Plot", "Arguments to plotting device") @argument( '-w', '--width', type=float, help='Width of plotting device in R.' ) @argument( '-h', '--height', type=float, help='Height of plotting device in R.' ) @argument( '-p', '--pointsize', type=int, help='Pointsize of plotting device in R.' ) @argument( '-b', '--bg', help='Background of plotting device in R.' ) @argument_group("SVG", "SVG specific arguments") @argument( '--noisolation', help=textwrap.dedent(""" Disable SVG isolation in the Notebook. By default, SVGs are isolated to avoid namespace collisions between figures. Disabling SVG isolation allows to reference previous figures or share CSS rules across a set of SVGs."""), action='store_false', default=True, dest='isolate_svgs' ) @argument_group("PNG", "PNG specific arguments") @argument( '-u', '--units', type=str, choices=["px", "in", "cm", "mm"], help=textwrap.dedent(""" Units of png plotting device sent as an argument to *png* in R. One of ["px", "in", "cm", "mm"].""")) @argument( '-r', '--res', type=int, help=textwrap.dedent(""" Resolution of png plotting device sent as an argument to *png* in R. Defaults to 72 if *units* is one of ["in", "cm", "mm"].""") ) @argument( '--type', type=str, choices=['cairo', 'cairo-png', 'Xlib', 'quartz'], help=textwrap.dedent(""" Type device used to generate the figure. """)) @argument( '-c', '--converter', default=None, help=textwrap.dedent(""" Name of local converter to use. A converter contains the rules to convert objects back and forth between Python and R. If not specified/None, the defaut converter for the magic\'s module is used (that is rpy2\'s default converter + numpy converter + pandas converter if all three are available).""")) @argument( 'code', nargs='*', ) @needs_local_scope @line_cell_magic def R(self, line, cell=None, local_ns=None): """ Execute code in R, optionally returning results to the Python runtime. In line mode, this will evaluate an expression and convert the returned value to a Python object. The return value is determined by rpy2's behaviour of returning the result of evaluating the final expression. Multiple R expressions can be executed by joining them with semicolons:: In [9]: %R X=c(1,4,5,7); sd(X); mean(X) Out[9]: array([ 4.25]) In cell mode, this will run a block of R code. The resulting value is printed if it would be printed when evaluating the same code within a standard R REPL. Nothing is returned to python by default in cell mode:: In [10]: %%R ....: Y = c(2,4,3,9) ....: summary(lm(Y~X)) Call: lm(formula = Y ~ X) Residuals: 1 2 3 4 0.88 -0.24 -2.28 1.64 Coefficients: Estimate Std. Error t value Pr(>|t|) (Intercept) 0.0800 2.3000 0.035 0.975 X 1.0400 0.4822 2.157 0.164 Residual standard error: 2.088 on 2 degrees of freedom Multiple R-squared: 0.6993,Adjusted R-squared: 0.549 F-statistic: 4.651 on 1 and 2 DF, p-value: 0.1638 In the notebook, plots are published as the output of the cell:: %R plot(X, Y) will create a scatter plot of X bs Y. If cell is not None and line has some R code, it is prepended to the R code in cell. Objects can be passed back and forth between rpy2 and python via the -i -o flags in line:: In [14]: Z = np.array([1,4,5,10]) In [15]: %R -i Z mean(Z) Out[15]: array([ 5.]) In [16]: %R -o W W=Z*mean(Z) Out[16]: array([ 5., 20., 25., 50.]) In [17]: W Out[17]: array([ 5., 20., 25., 50.]) The return value is determined by these rules: * If the cell is not None (i.e., has contents), the magic returns None. * If the final line results in a NULL value when evaluated by rpy2, then None is returned. * No attempt is made to convert the final value to a structured array. Use %Rget to push a structured array. * If the -n flag is present, there is no return value. * A trailing ';' will also result in no return value as the last value in the line is an empty string. """ args = parse_argstring(self.R, line) # arguments 'code' in line are prepended to # the cell lines if cell is None: code = '' return_output = True line_mode = True else: code = cell return_output = False line_mode = False code = ' '.join(args.code) + code # if there is no local namespace then default to an empty dict if local_ns is None: local_ns = {} if args.converter is None: converter = self.converter else: try: converter = local_ns[args.converter] except KeyError: try: converter = self.shell.user_ns[args.converter] except KeyError: raise NameError( "name '%s' is not defined" % args.converter ) if not isinstance(converter, Converter): raise TypeError("'%s' must be a %s object (but it is a %s)." % (args.converter, Converter, type(localconverter))) if args.input: for input in ','.join(args.input).split(','): try: val = local_ns[input] except KeyError: try: val = self.shell.user_ns[input] except KeyError: raise NameError("name '%s' is not defined" % input) with localconverter(converter) as cv: ro.r.assign(input, val) tmpd = self.setup_graphics(args) text_output = '' try: if line_mode: for line in code.split(';'): text_result, result, visible = self.eval(line) text_output += text_result if text_result: # The last line printed something to the console so # we won't return it. return_output = False else: text_result, result, visible = self.eval(code) text_output += text_result if visible: with contextlib.ExitStack() as stack: if self.cache_display_data: stack.enter_context( rpy2.rinterface_lib .callbacks .obj_in_module(rpy2.rinterface_lib .callbacks, 'consolewrite_print', self.write_console_regular)) ro.r.show(result) text_output += self.flush() except RInterpreterError as e: # TODO: Maybe we should make this red or something? print(e.stdout) if not e.stdout.endswith(e.err): print(e.err) if tmpd: rmtree(tmpd) return finally: if self.device in ['png', 'svg']: ro.r('dev.off()') if text_output: # display_data.append(('RMagic.R', {'text/plain':text_output})) displaypub.publish_display_data( data={'text/plain': text_output}, source='RMagic.R') # publish the R images if self.device in ['png', 'svg']: display_data, md = self.publish_graphics(tmpd, args.isolate_svgs) for tag, disp_d in display_data: displaypub.publish_display_data(data=disp_d, source=tag, metadata=md) # kill the temporary directory - currently created only for "svg" # and "png" (else it's None) rmtree(tmpd) if args.output: with localconverter(converter) as cv: for output in ','.join(args.output).split(','): output_ipy = ro.globalenv.find(output) self.shell.push({output: output_ipy}) # this will keep a reference to the display_data # which might be useful to other objects who happen to use # this method if self.cache_display_data: self.display_cache = display_data # We're in line mode and return_output is still True, # so return the converted result if return_output and not args.noreturn: if result is not ri.NULL: with localconverter(converter) as cv: res = cv.rpy2py(result) return res __doc__ = __doc__.format( R_DOC=' '*8 + RMagics.R.__doc__, RPUSH_DOC=' '*8 + RMagics.Rpush.__doc__, RPULL_DOC=' '*8 + RMagics.Rpull.__doc__, RGET_DOC=' '*8 + RMagics.Rget.__doc__ ) def load_ipython_extension(ip): """Load the extension in IPython.""" ip.register_magics(RMagics) rpy2-3.2.6/rpy2/interactive/0000755000175000017500000000000013615572523017006 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/interactive/packages.py0000644000175000017500000000313213576515767021152 0ustar laurentlaurent00000000000000from rpy2.robjects.packages import importr as _importr from rpy2.robjects.packages import data import rpy2.robjects.help as rhelp from rpy2.rinterface import baseenv from os import linesep from collections import OrderedDict import re class Packages(object): __instance = None def __new__(cls): if cls.__instance is None: cls.__instance = object.__new__(cls) return cls.__instance def __setattr__(self, name, value): raise AttributeError("Attributes cannot be set. Use 'importr'") packages = Packages() _loaded_namespaces = baseenv['loadedNamespaces'] def importr(packname, newname = None, verbose = False): """ Wrapper around rpy2.robjects.packages.importr, adding the following feature(s): - package instance added to the pseudo-module 'packages' """ assert isinstance(packname, str) packinstance = _importr(packname, on_conflict = 'warn') # fix the package name (dots possible in R package names) if newname is None: newname = packname.replace('.', '_') Packages().__dict__[newname] = packinstance ## Currently too slow for a serious usage: R's introspection ## of S4 classes is not fast enough # d = {} # for cn in methods.get_classnames(packname): # class AutoS4(RS4): # __metaclass__ = methods.RS4Auto_Type # __rpackagename__ = packname # __rname__ = cn # newcn = cn.replace('.', '_') # d[newcn] = AutoS4 # S4Classes().__dict__[newname] = d return packinstance for packname in _loaded_namespaces(): importr(packname) rpy2-3.2.6/rpy2/interactive/__init__.py0000644000175000017500000000267413576515767021145 0ustar laurentlaurent00000000000000""" Package to interface with R, with a focus on interactive usage (REPL approach to code-writing), although the package will work in non-interactive settings. The package aims at being simple rather than exhaustively complete (rpy2.robjects, or rpy2.rinterface, can be used if needed), providing a comfortable experience with autocompletion-capable python consoles. """ from collections import OrderedDict from rpy2.robjects.packages import _loaded_namespaces from rpy2.robjects.vectors import IntVector, FloatVector, ComplexVector from rpy2.robjects.vectors import Array, Matrix from rpy2.robjects.vectors import StrVector from rpy2.robjects.vectors import ListVector, DataFrame from rpy2.robjects.environments import Environment from rpy2.rinterface import NULL from rpy2.robjects import Formula, RS4 from rpy2.robjects import methods from rpy2.robjects import conversion from rpy2.robjects import help as rhelp from rpy2.robjects.language import eval from . import process_revents as revents from os import linesep import re class S4Classes(object): """ *Very* experimental attempt at getting the S4 classes dynamically mirrored. """ __instance = None def __new__(cls): if cls.__instance is None: cls.__instance = object.__new__(cls) return cls.__instance def __setattr__(self, name, value): raise AttributeError("Attributes cannot be set. Use 'importr'") #classes = S4Classes() #revents.start() rpy2-3.2.6/rpy2/interactive/process_revents.py0000644000175000017500000000334513576515767022626 0ustar laurentlaurent00000000000000"""This module runs continuous updates for R, such as redrawing graphs when the plot window is resized. Use the start() and stop() functions to turn updates on and off. """ from rpy2.rinterface import process_revents import time import warnings import threading class _EventProcessorThread(threading.Thread): """ Call rinterface.process_revents(), pausing for at least EventProcessor.interval between calls. """ _continue = True def run(self): while self._continue: process_revents() time.sleep(EventProcessor.interval) class EventProcessor(object): """ Processor for R events (Singleton class) """ interval = 0.2 daemon_thread = True name_thread = 'rpy2_process_revents' _thread = None _instance = None def __new__(cls): if cls._instance is None: cls._instance = object.__new__(cls) return cls._instance def start(self): """ start the event processor """ if (self._thread is not None) and (self._thread.is_alive()): raise warnings.warn("Processing of R events already started.") else: self._thread = _EventProcessorThread(name = self.name_thread) self._thread.setDaemon(self.daemon_thread) self._thread.start() def stop(self): """ stop the event processor """ self._thread._continue = False self._thread.join() thread = property(lambda self: self._thread, None, None, "Thread that processes the events.") def start(): """ Start the threaded processing of R events. """ EventProcessor().start() def stop(): """ Stop the threaded processing of R events. """ EventProcessor().stop() rpy2-3.2.6/rpy2/rinterface.py0000644000175000017500000011023713615570032017162 0ustar laurentlaurent00000000000000import abc import atexit import os import math import typing from rpy2.rinterface_lib import openrlib import rpy2.rinterface_lib._rinterface_capi as _rinterface import rpy2.rinterface_lib.embedded as embedded import rpy2.rinterface_lib.conversion as conversion import rpy2.rinterface_lib.memorymanagement as memorymanagement import rpy2.rinterface_lib.na_values as na_values import rpy2.rinterface_lib.bufferprotocol as bufferprotocol import rpy2.rinterface_lib.sexp as sexp if os.name == 'nt': import rpy2.rinterface_lib.embedded_mswin as embedded_mswin embedded._initr = embedded_mswin._initr Sexp = sexp.Sexp StrSexpVector = sexp.StrSexpVector CharSexp = sexp.CharSexp SexpVector = sexp.SexpVector RTYPES = sexp.RTYPES unserialize = sexp.unserialize _cdata_res_to_rinterface = conversion._cdata_res_to_rinterface _evaluated_promise = _rinterface._evaluated_promise R_NilValue = openrlib.rlib.R_NilValue endr = embedded.endr @_cdata_res_to_rinterface def parse(text: str, num: int = -1): """Parse a string as R code. :param:`text` A string with R code to parse. :param:`num` The maximum number of lines to parse. If -1, no limit is applied. """ if not isinstance(text, str): raise TypeError('text must be a string.') robj = StrSexpVector([text]) return _rinterface._parse(robj.__sexp__._cdata, num) def evalr(source: str, maxlines: int = -1) -> sexp.Sexp: """Evaluate a string as R code. Evaluate a string as R just as it would happen when writing code in an R terminal. :param:`text` A string to be evaluated as R code. :param:`maxlines` The maximum number of lines to parse. If -1, no limit is applied.""" res = parse(source, num=maxlines) res = baseenv['eval'](res) return res def vector_memoryview(obj: sexp.SexpVector, sizeof_str: str, cast_str: str) -> memoryview: """ - sizeof_str: type in a string to use with ffi.sizeof() (for example "int") - cast_str: type in a string to use with memoryview.cast() (for example "i") """ b = openrlib.ffi.buffer( obj._R_GET_PTR(obj.__sexp__._cdata), openrlib.ffi.sizeof(sizeof_str) * len(obj)) shape = bufferprotocol.getshape(obj.__sexp__._cdata) # One could have expected to only need builtin Python # and do something like # ``` # mv = memoryview(b).cast(cast_str, shape, order='F') # ``` # but Python does not handle FORTRAN-ordered arrays without having # to write C extensions. We have to use numpy. # TODO: Having numpy a requirement just for this is a problem. # TODO: numpy needed for memoryview # (as long as https://bugs.python.org/issue34778 not resolved) import numpy a = numpy.frombuffer(b, dtype=cast_str).reshape(shape, order='F') mv = memoryview(a) return mv class NULLType(sexp.Sexp, metaclass=na_values.Singleton): """A singleton class for R's NULL.""" def __init__(self): embedded.assert_isready() super().__init__( sexp.Sexp( _rinterface.UnmanagedSexpCapsule( openrlib.rlib.R_NilValue ) ) ) def __bool__(self) -> bool: """This is always False.""" return False @property def __sexp__(self) -> _rinterface.SexpCapsule: return self._sexpobject @property def rid(self) -> int: return self._sexpobject.rid class _MissingArgType(sexp.Sexp, metaclass=na_values.Singleton): def __init__(self): embedded.assert_isready() super().__init__( sexp.Sexp( _rinterface.UnmanagedSexpCapsule( openrlib.rlib.R_MissingArg ) ) ) def __bool__(self) -> bool: """This is always False.""" return False @property def __sexp__(self) -> _rinterface.SexpCapsule: return self._sexpobject class SexpSymbol(sexp.Sexp): """An unevaluated R symbol.""" def __init__(self, obj): if isinstance(obj, Sexp) or isinstance(obj, _rinterface.SexpCapsule): super().__init__(obj) elif isinstance(obj, str): name_cdata = _rinterface.ffi.new('char []', obj.encode('utf-8')) sexp = _rinterface.SexpCapsule( openrlib.rlib.Rf_install(name_cdata)) super().__init__(sexp) else: raise TypeError( 'The constructor must be called ' 'with that is an instance of rpy2.rinterface.sexp.Sexp ' 'or an instance of rpy2.rinterface._rinterface.SexpCapsule') def __str__(self) -> str: return conversion._cchar_to_str( openrlib._STRING_VALUE( self._sexpobject._cdata ) ) class SexpEnvironment(sexp.Sexp): """Proxy for an R "environment" object. An R "environment" object can be thought of as a mix of a mapping (like a `dict`) and a scope. To make it more "Pythonic", both aspects are kept separate and the method `__getitem__` will get an item as it would for a Python `dict` while the method `find` will get an item as if it was a scope. As soon as R is initialized the following main environments become available to the user: - `globalenv`: The "workspace" for the current R process. This can be thought of as when `__name__ == '__main__'` in Python. - `baseenv`: The namespace of R's "base" package. """ @_cdata_res_to_rinterface @_evaluated_promise def find(self, key: str, wantfun: bool = False) -> sexp.Sexp: """Find an item, starting with this R environment. Raises a `KeyError` if the key cannot be found. This method is called `find` because it is somewhat different from the method :meth:`get` in Python mappings such :class:`dict`. This is looking for a key across enclosing environments, returning the first key found.""" if not isinstance(key, str): raise TypeError('The key must be a non-empty string.') elif not len(key): raise ValueError('The key must be a non-empty string.') with memorymanagement.rmemory() as rmemory: symbol = rmemory.protect( openrlib.rlib.Rf_install(conversion._str_to_cchar(key)) ) if wantfun: # One would expect this to be like # res = _rinterface._findfun(symbol, self.__sexp__._cdata) # but R's findfun will segfault if the symbol is not in # the environment. :/ rho = self while rho.rid != emptyenv.rid: res = _rinterface._findVarInFrame(symbol, rho.__sexp__._cdata) if _rinterface._TYPEOF(res) in (openrlib.rlib.CLOSXP, openrlib.rlib.BUILTINSXP): break # TODO: move check of R_UnboundValue to _rinterface ? res = openrlib.rlib.R_UnboundValue rho = rho.enclos else: res = _rinterface._findvar(symbol, self.__sexp__._cdata) # TODO: move check of R_UnboundValue to _rinterface ? if res == openrlib.rlib.R_UnboundValue: raise KeyError("'%s' not found" % key) return res @_cdata_res_to_rinterface @_evaluated_promise def __getitem__(self, key: str) -> typing.Any: if not isinstance(key, str): raise TypeError('The key must be a non-empty string.') elif not len(key): raise ValueError('The key must be a non-empty string.') with memorymanagement.rmemory() as rmemory: symbol = rmemory.protect( openrlib.rlib.Rf_install(conversion._str_to_cchar(key)) ) res = _rinterface._findVarInFrame(symbol, self.__sexp__._cdata) # TODO: move check of R_UnboundValue to _rinterface if res == openrlib.rlib.R_UnboundValue: raise KeyError("'%s' not found" % key) return res def __setitem__(self, key: str, value) -> None: # TODO: move body to _rinterface-level function if not isinstance(key, str): raise TypeError('The key must be a non-empty string.') elif not len(key): raise ValueError('The key must be a non-empty string.') if (self.__sexp__._cdata == openrlib.rlib.R_BaseEnv) or \ (self.__sexp__._cdata == openrlib.rlib.R_EmptyEnv): raise ValueError('Cannot remove variables from the base or ' 'empty environments.') # TODO: call to Rf_duplicate needed ? with memorymanagement.rmemory() as rmemory: symbol = rmemory.protect( openrlib.rlib.Rf_install(conversion._str_to_cchar(key)) ) cdata = rmemory.protect(conversion._get_cdata(value)) cdata_copy = rmemory.protect( openrlib.rlib.Rf_duplicate(cdata) ) openrlib.rlib.Rf_defineVar(symbol, cdata_copy, self.__sexp__._cdata) def __len__(self) -> int: with memorymanagement.rmemory() as rmemory: symbols = rmemory.protect( openrlib.rlib.R_lsInternal(self.__sexp__._cdata, openrlib.rlib.TRUE) ) n = openrlib.rlib.Rf_xlength(symbols) return n def __delitem__(self, key: str) -> None: # Testing that key is a non-empty string is implicitly # performed when checking that the key is in the environment. if key not in self: raise KeyError("'%s' not found" % key) if self.__sexp__ == baseenv.__sexp__: raise ValueError('Values from the R base environment ' 'cannot be removed.') # TODO: also check it is not R_EmpyEnv or R_BaseNamespace if self.is_locked(): ValueError('Cannot remove an item from a locked ' 'environment.') with memorymanagement.rmemory() as rmemory: key_cdata = rmemory.protect( openrlib.rlib.Rf_mkString(conversion._str_to_cchar(key)) ) _rinterface._remove(key_cdata, self.__sexp__._cdata, openrlib.rlib.Rf_ScalarLogical( openrlib.rlib.FALSE)) @_cdata_res_to_rinterface def frame(self) -> 'typing.Union[NULLType, SexpEnvironment]': """Get the parent frame of the environment.""" return openrlib.rlib.FRAME(self.__sexp__._cdata) @property @_cdata_res_to_rinterface def enclos(self) -> 'typing.Union[NULLType, SexpEnvironment]': """Get or set the enclosing environment.""" return openrlib.rlib.ENCLOS(self.__sexp__._cdata) @enclos.setter def enclos(self, value: 'SexpEnvironment') -> None: assert isinstance(value, SexpEnvironment) openrlib.rlib.SET_ENCLOS(self.__sexp__._cdata, value.__sexp__.cdata) def keys(self) -> typing.Generator[str, None, None]: """Generator over the keys (symbols) in the environment.""" with memorymanagement.rmemory() as rmemory: symbols = rmemory.protect( openrlib.rlib.R_lsInternal(self.__sexp__._cdata, openrlib.rlib.TRUE) ) n = openrlib.rlib.Rf_xlength(symbols) res = [] for i in range(n): res.append(_rinterface._string_getitem(symbols, i)) for e in res: yield e def __iter__(self) -> typing.Generator[str, None, None]: """See method `keys()`.""" return self.keys() def is_locked(self) -> bool: return openrlib.rlib.R_EnvironmentIsLocked( self.__sexp__._cdata) class SexpPromise(Sexp): @_cdata_res_to_rinterface def eval(self, env: typing.Optional[SexpEnvironment] = None) -> sexp.Sexp: """"Evalute the R "promise". :param:`env` The environment in which to evaluate the promise. """ if not env: env = embedded.globalenv return openrlib.rlib.Rf_eval(self.__sexp__._cdata, env) class NumpyArrayInterface(abc.ABC): """Numpy-specific API for accessing the content of a numpy array. This interface implements version 3 of Numpy's `__array_interface__` and is only available / possible for some of the R vectors.""" @property def __array_interface__(self) -> dict: """Return an `__array_interface__` version 3. Note that the pointer returned in the items 'data' corresponds to a memory area under R's memory management and that it will become invalid once the area once R frees the object. It is safer to keep the rpy2 object proxying the R object alive for the duration the pointer is used in Python / numpy.""" shape = bufferprotocol.getshape(self.__sexp__._cdata) data = openrlib.ffi.buffer(self._R_GET_PTR(self.__sexp__._cdata)) strides = bufferprotocol.getstrides(self.__sexp__._cdata, shape, self._R_SIZEOF_ELT) return {'shape': shape, 'typestr': self._NP_TYPESTR, 'strides': strides, 'data': data, 'version': 3} class ByteSexpVector(SexpVector, NumpyArrayInterface): """Array of bytes. This is the R equivalent to a Python :class:`bytesarray`. """ _R_TYPE = openrlib.rlib.RAWSXP _R_SIZEOF_ELT = _rinterface.ffi.sizeof('char') _NP_TYPESTR = '|u1' _R_GET_PTR = staticmethod(openrlib.RAW) @staticmethod def _CAST_IN(x: typing.Any) -> int: if isinstance(x, int): if x > 255: raise ValueError('byte must be in range(0, 256)') res = x elif isinstance(x, (bytes, bytearray)): if len(x) != 1: raise ValueError('byte must be a single character') res = ord(x) else: raise ValueError('byte must be an integer [0, 255] or a ' 'single byte character') return res @staticmethod def _R_VECTOR_ELT(x, i: int) -> None: return openrlib.RAW(x)[i] @staticmethod def _R_SET_VECTOR_ELT(x, i: int, val) -> None: openrlib.RAW(x)[i] = val def __getitem__(self, i: int) -> typing.Union[int, 'ByteSexpVector']: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) res = openrlib.RAW_ELT(cdata, i_c) elif isinstance(i, slice): res = type(self).from_iterable( [openrlib.RAW_ELT( cdata, i_c ) for i_c in range(*i.indices(len(self))) ] ) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) return res def __setitem__(self, i: int, value) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) openrlib.RAW(cdata)[i_c] = self._CAST_IN(value) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): if v > 255: raise ValueError('byte must be in range(0, 256)') openrlib.RAW(cdata)[i_c] = self._CAST_IN(v) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) class BoolSexpVector(SexpVector, NumpyArrayInterface): """Array of booleans. Note that R is internally storing booleans as integers to allow an additional "NA" value to represent missingness.""" _R_TYPE = openrlib.rlib.LGLSXP _R_SIZEOF_ELT = _rinterface.ffi.sizeof('Rboolean') _NP_TYPESTR = '|i' _R_VECTOR_ELT = openrlib.LOGICAL_ELT _R_SET_VECTOR_ELT = openrlib.SET_LOGICAL_ELT _R_GET_PTR = staticmethod(openrlib.LOGICAL) @staticmethod def _CAST_IN(x): if x is None or x == openrlib.rlib.R_NaInt: return NA_Logical else: return bool(x) def __getitem__(self, i: int) -> typing.Union[typing.Optional[bool], 'BoolSexpVector']: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) elt = openrlib.LOGICAL_ELT(cdata, i_c) res = na_values.NA_Logical if elt == NA_Logical else bool(elt) elif isinstance(i, slice): res = type(self).from_iterable( [openrlib.LOGICAL_ELT(cdata, i_c) for i_c in range(*i.indices(len(self)))] ) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) return res def __setitem__(self, i: int, value) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) openrlib.SET_LOGICAL_ELT(cdata, i_c, int(value)) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): openrlib.SET_LOGICAL_ELT(cdata, i_c, int(v)) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) def memoryview(self) -> memoryview: return vector_memoryview(self, 'int', 'i') def nullable_int(v): if type(v) is float and math.isnan(v): return openrlib.rlib.R_NaInt else: return int(v) class IntSexpVector(SexpVector, NumpyArrayInterface): _R_TYPE = openrlib.rlib.INTSXP _R_SET_VECTOR_ELT = openrlib.SET_INTEGER_ELT _R_VECTOR_ELT = openrlib.INTEGER_ELT _R_SIZEOF_ELT = _rinterface.ffi.sizeof('int') _NP_TYPESTR = '|i' _R_GET_PTR = staticmethod(openrlib.INTEGER) _CAST_IN = staticmethod(nullable_int) def __getitem__(self, i: int) -> typing.Union[int, 'IntSexpVector']: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) res = openrlib.INTEGER_ELT(cdata, i_c) if res == NA_Integer: res = NA_Integer elif isinstance(i, slice): res = type(self).from_iterable( [openrlib.INTEGER_ELT( cdata, i_c ) for i_c in range(*i.indices(len(self)))] ) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) return res def __setitem__(self, i: int, value) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) openrlib.SET_INTEGER_ELT(cdata, i_c, int(value)) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): openrlib.SET_INTEGER_ELT(cdata, i_c, int(v)) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) def memoryview(self) -> memoryview: return vector_memoryview(self, 'int', 'i') class FloatSexpVector(SexpVector, NumpyArrayInterface): _R_TYPE = openrlib.rlib.REALSXP _R_VECTOR_ELT = openrlib.REAL_ELT _R_SET_VECTOR_ELT = openrlib.SET_REAL_ELT _R_SIZEOF_ELT = _rinterface.ffi.sizeof('double') _NP_TYPESTR = '|d' _CAST_IN = staticmethod(float) _R_GET_PTR = staticmethod(openrlib.REAL) def __getitem__(self, i: int) -> typing.Union[float, 'FloatSexpVector']: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) res = openrlib.REAL_ELT(cdata, i_c) elif isinstance(i, slice): res = type(self).from_iterable( [openrlib.REAL_ELT( cdata, i_c) for i_c in range(*i.indices(len(self)))] ) else: raise TypeError('Indices must be integers or slices, not %s' % type(i)) return res def __setitem__(self, i: int, value) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) openrlib.SET_REAL_ELT(cdata, i_c, float(value)) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): openrlib.SET_REAL_ELT(cdata, i_c, float(v)) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) def memoryview(self) -> memoryview: return vector_memoryview(self, 'double', 'd') class ComplexSexpVector(SexpVector): _R_TYPE = openrlib.rlib.CPLXSXP _R_GET_PTR = staticmethod(openrlib.COMPLEX) _R_SIZEOF_ELT = _rinterface.ffi.sizeof('Rcomplex') @staticmethod def _R_VECTOR_ELT(x, i): return openrlib.COMPLEX(x)[i] @staticmethod def _R_SET_VECTOR_ELT(x, i, v): openrlib.COMPLEX(x).__setitem__(i, v) @staticmethod def _CAST_IN(x): if isinstance(x, complex): res = (x.real, x.imag) else: try: res = (x.r, x.i) except AttributeError: raise TypeError( 'Unable to turn value into an R complex number.' ) return res def __getitem__(self, i: int) -> typing.Union[complex, 'ComplexSexpVector']: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) _ = openrlib.COMPLEX_ELT(cdata, i_c) res = complex(_.r, _.i) elif isinstance(i, slice): res = type(self).from_iterable( [openrlib.COMPLEX_ELT( cdata, i_c) for i_c in range(*i.indices(len(self)))] ) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) return res def __setitem__(self, i: int, value) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) openrlib.COMPLEX(cdata)[i_c] = self._CAST_IN(value) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): openrlib.COMPLEX(cdata)[i_c] = self._CAST_IN(v) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) class ListSexpVector(SexpVector): """R list. An R list an R vector (array) that is similar to a Python list in the sense that items in the list can be of any type, whereas most other R vectors are homogeneous (all items are of the same type). """ _R_TYPE = openrlib.rlib.VECSXP _R_GET_PTR = staticmethod(openrlib._VECTOR_PTR) _R_SIZEOF_ELT = None _R_VECTOR_ELT = openrlib.rlib.VECTOR_ELT _R_SET_VECTOR_ELT = openrlib.rlib.SET_VECTOR_ELT _CAST_IN = staticmethod(conversion._get_cdata) class PairlistSexpVector(SexpVector): """R pairlist. A R pairlist is rarely used outside of R's internal libraries and a relatively small number of use cases. It is essentially a LISP-like list of (name, value) pairs. """ _R_TYPE = openrlib.rlib.LISTSXP _R_GET_PTR = None _R_SIZEOF_ELT = None _R_VECTOR_ELT = None _R_SET_VECTOR_ELT = None _CAST_IN = staticmethod(conversion._get_cdata) def __getitem__(self, i: int) -> 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)) rlib.SET_STRING_ELT( res_name, 0, rlib.PRINTNAME(rlib.TAG(item_cdata))) 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 @classmethod @_cdata_res_to_rinterface def from_iterable(cls, iterable, cast_in=None): raise NotImplementedError() class ExprSexpVector(SexpVector): _R_TYPE = openrlib.rlib.EXPRSXP _R_GET_PTR = None _CAST_IN = None _R_SIZEOF_ELT = None _R_VECTOR_ELT = openrlib.rlib.VECTOR_ELT _R_SET_VECTOR_ELT = None class LangSexpVector(SexpVector): _R_TYPE = openrlib.rlib.LANGSXP _R_GET_PTR = None _CAST_IN = None _R_SIZEOF_ELT = None _R_VECTOR_ELT = None _R_SET_VECTOR_ELT = None @_cdata_res_to_rinterface def __getitem__(self, i: int): cdata = self.__sexp__._cdata i_c = _rinterface._python_index_to_c(cdata, i) return openrlib.rlib.CAR( openrlib.rlib.Rf_nthcdr(cdata, i_c) ) def __setitem__(self, i: int, value) -> None: cdata = self.__sexp__._cdata i_c = _rinterface._python_index_to_c(cdata, i) openrlib.rlib.SETCAR( openrlib.rlib.Rf_nthcdr(cdata, i_c), value.__sexp__._cdata ) class SexpClosure(Sexp): @_cdata_res_to_rinterface def __call__(self, *args, **kwargs) -> Sexp: error_occured = _rinterface.ffi.new('int *', 0) with memorymanagement.rmemory() as rmemory: call_r = rmemory.protect( _rinterface.build_rcall(self.__sexp__._cdata, args, kwargs.items())) res = rmemory.protect( openrlib.rlib.R_tryEval( call_r, embedded.globalenv.__sexp__._cdata, error_occured)) if error_occured[0]: raise embedded.RRuntimeError(_rinterface._geterrmessage()) return res @_cdata_res_to_rinterface def rcall(self, keyvals, environment: SexpEnvironment): """Call/evaluate an R function. Args: - keyvals: a sequence of key/value (name/parameter) pairs. A name/parameter that is None will indicated an unnamed parameter. Like in R, keys/names do not have to be unique, partial matching can be used, and named/unnamed parameters can occur at any position in the sequence. - environment: a R environment in which to evaluate the function. """ # TODO: check keyvals are pairs ? assert isinstance(environment, SexpEnvironment) error_occured = _rinterface.ffi.new('int *', 0) with memorymanagement.rmemory() as rmemory: call_r = rmemory.protect( _rinterface.build_rcall(self.__sexp__._cdata, [], keyvals)) res = rmemory.protect( openrlib.rlib.R_tryEval(call_r, environment.__sexp__._cdata, error_occured)) if error_occured[0]: raise embedded.RRuntimeError(_rinterface._geterrmessage()) return res @property @_cdata_res_to_rinterface def closureenv(self) -> SexpEnvironment: """Closure of the R function.""" return openrlib.rlib.CLOENV(self.__sexp__._cdata) class SexpS4(Sexp): """R "S4" object.""" pass # TODO: clean up def make_extptr(obj, tag, protected): if protected is None: cdata_protected = openrlib.rlib.R_NilValue else: try: cdata_protected = protected.__sexp__._cdata except AttributeError: raise TypeError('Argument protected must inherit from %s' % type(Sexp)) ptr = _rinterface.ffi.new_handle(obj) with memorymanagement.rmemory() as rmemory: cdata = rmemory.protect( openrlib.rlib.R_MakeExternalPtr( ptr, tag, cdata_protected)) openrlib.rlib.R_RegisterCFinalizer( cdata, (_rinterface._capsule_finalizer_c if _rinterface._capsule_finalizer_c else _rinterface._capsule_finalizer)) res = _rinterface.SexpCapsuleWithPassenger(cdata, obj, ptr) return res class SexpExtPtr(Sexp): TYPE_TAG = 'Python' @classmethod def from_pyobject(cls, func, tag: str = TYPE_TAG, protected=None): if not isinstance(tag, str): raise TypeError('The tag must be a string.') scaps = make_extptr(func, conversion._str_to_charsxp(cls.TYPE_TAG), protected) res = cls(scaps) if tag != cls.TYPE_TAG: res.TYPE_TAG = tag return res # TODO: Only use rinterface-level ? conversion._R_RPY2_MAP.update({ openrlib.rlib.NILSXP: NULLType, openrlib.rlib.EXPRSXP: ExprSexpVector, openrlib.rlib.LANGSXP: LangSexpVector, openrlib.rlib.ENVSXP: SexpEnvironment, openrlib.rlib.RAWSXP: ByteSexpVector, openrlib.rlib.LGLSXP: BoolSexpVector, openrlib.rlib.INTSXP: IntSexpVector, openrlib.rlib.REALSXP: FloatSexpVector, openrlib.rlib.CPLXSXP: ComplexSexpVector, openrlib.rlib.STRSXP: StrSexpVector, openrlib.rlib.VECSXP: ListSexpVector, openrlib.rlib.LISTSXP: PairlistSexpVector, openrlib.rlib.CLOSXP: SexpClosure, openrlib.rlib.BUILTINSXP: SexpClosure, openrlib.rlib.SPECIALSXP: SexpClosure, openrlib.rlib.EXTPTRSXP: SexpExtPtr, openrlib.rlib.SYMSXP: SexpSymbol, openrlib.rlib.S4SXP: SexpS4 }) conversion._R_RPY2_DEFAULT_MAP = Sexp conversion._PY_RPY2_MAP.update({ int: conversion._int_to_sexp, float: conversion._float_to_sexp, complex: conversion._complex_to_sexp }) conversion._PY_R_MAP.update({ _rinterface.ffi.CData: False, # integer int: conversion._int_to_sexp, na_values.NAIntegerType: conversion._int_to_sexp, # float float: conversion._float_to_sexp, na_values.NARealType: conversion._float_to_sexp, # boolean bool: conversion._bool_to_sexp, na_values.NALogicalType: conversion._bool_to_sexp, # string str: conversion._str_to_sexp, sexp.CharSexp: None, na_values.NACharacterType: None, # complex complex: conversion._complex_to_sexp, na_values.NAComplexType: conversion._complex_to_sexp, # None type(None): lambda x: openrlib.rlib.R_NilValue}) def vector(iterable, rtype: RTYPES) -> SexpVector: """Create an R vector. While the different types of R vectors all have their own class, the creation of array in Python is often available through factory function that accept the type of the array as a parameters. This function is providing a similar functionality for R vectors.""" error = False try: cls = conversion._R_RPY2_MAP[rtype] except KeyError: error = True if not error and not issubclass(cls, SexpVector): error = True if error: raise ValueError( 'Unable to build a vector from type "%s"' % RTYPES(rtype)) return cls.from_iterable(iterable) class RRuntimeWarning(RuntimeWarning): pass emptyenv = None baseenv = None globalenv = None NULL = None MissingArg = None NA_Character = None NA_Integer = None NA_Logical = None NA = None NA_Real = None NA_Complex = None def initr_simple() -> int: """Initialize R's embedded C library.""" with openrlib.rlock: status = embedded._initr() atexit.register(endr, 0) _rinterface._register_external_symbols() _post_initr_setup() return status def initr_checkenv() -> typing.Union[int, None]: # Force the internal initialization flag if there is an environment # variable that indicates that R was alreay initialized in the current # process. status = None with openrlib.rlock: if embedded.is_r_externally_initialized(): embedded.setinitialized() else: status = embedded._initr() embedded.set_python_process_info() _rinterface._register_external_symbols() _post_initr_setup() return status initr = initr_checkenv def _post_initr_setup() -> None: embedded.emptyenv = SexpEnvironment( _rinterface.SexpCapsule(openrlib.rlib.R_EmptyEnv) ) global emptyenv emptyenv = embedded.emptyenv embedded.baseenv = SexpEnvironment( _rinterface.SexpCapsule(openrlib.rlib.R_BaseEnv) ) global baseenv baseenv = embedded.baseenv embedded.globalenv = SexpEnvironment( _rinterface.SexpCapsule(openrlib.rlib.R_GlobalEnv) ) global globalenv globalenv = embedded.globalenv global NULL NULL = NULLType() global MissingArg MissingArg = _MissingArgType() global NA_Character na_values.NA_Character = na_values.NACharacterType() NA_Character = na_values.NA_Character global NA_Integer na_values.NA_Integer = na_values.NAIntegerType(openrlib.rlib.R_NaInt) NA_Integer = na_values.NA_Integer global NA_Logical, NA na_values.NA_Logical = na_values.NALogicalType(openrlib.rlib.R_NaInt) NA_Logical = na_values.NA_Logical NA = NA_Logical global NA_Real na_values.NA_Real = na_values.NARealType(openrlib.rlib.R_NaReal) NA_Real = na_values.NA_Real global NA_Complex na_values.NA_Complex = na_values.NAComplexType( _rinterface.ffi.new( 'Rcomplex *', [openrlib.rlib.R_NaReal, openrlib.rlib.R_NaReal]) ) NA_Complex = na_values.NA_Complex def rternalize(function: typing.Callable) -> SexpClosure: """ Takes an arbitrary Python function and wrap it in such a way that it can be called from the R side. """ assert callable(function) rpy_fun = SexpExtPtr.from_pyobject(function) # TODO: this is a hack. Find a better way. template = parse(""" function(...) { .External(".Python", foo, ...); } """) template[0][2][1][2] = rpy_fun # TODO: use lower-level eval ? res = baseenv['eval'](template) # TODO: hack to prevent the nested function from having its # refcount down to zero when returning res.__nested_sexp__ = rpy_fun.__sexp__ return res def process_revents() -> None: """Process R events. Calling this function a regular interval can help ensure that R is processing input events (e.g., resizing of a window with graphics).""" openrlib.rlib.rpy2_runHandlers(openrlib.rlib.R_InputHandlers) rpy2-3.2.6/rpy2/situation.py0000644000175000017500000002261413615570032017060 0ustar laurentlaurent00000000000000""" This module is currently primarily intended to be used as a script. It will print information about the rpy2's environment (Python version, R version, rpy2 version, etc...). """ import argparse import enum import os import shlex import subprocess import sys from typing import Optional import warnings try: import rpy2 # noqa:F401 has_rpy2 = True except ImportError: has_rpy2 = False class CFFI_MODE(enum.Enum): API = 'API' ABI = 'ABI' BOTH = 'BOTH' ANY = 'ANY' def get_cffi_mode(default=CFFI_MODE.ANY): cffi_mode = os.environ.get('RPY2_CFFI_MODE', '') for m in (CFFI_MODE.API, CFFI_MODE.ABI, CFFI_MODE.BOTH, CFFI_MODE.ANY): if cffi_mode.upper() == m.value: return m return default def assert_python_version(): if not (sys.version_info[0] >= 3 and sys.version_info[1] >= 3): raise RuntimeError( 'Python >=3.3 is required to run rpy2') def r_version_from_subprocess(): try: tmp = subprocess.check_output(('R', '--version')) except Exception: # FileNotFoundError, WindowsError, etc return None r_version = tmp.decode('ascii', 'ignore').split(os.linesep) if r_version[0].startswith('WARNING'): r_version = r_version[1] else: r_version = r_version[0].strip() return r_version def r_home_from_subprocess() -> Optional[str]: """Return the R home directory from calling 'R RHOME'.""" try: tmp = subprocess.check_output(('R', 'RHOME'), universal_newlines=True) except Exception: # FileNotFoundError, WindowsError, etc return None r_home = tmp.split(os.linesep) if r_home[0].startswith('WARNING'): res = r_home[1] else: res = r_home[0].strip() return res def r_home_from_registry() -> Optional[str]: """Return the R home directory from the Windows Registry.""" try: import winreg except ImportError: import _winreg as winreg try: hkey = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, 'Software\\R-core\\R', 0, winreg.KEY_QUERY_VALUE) r_home = winreg.QueryValueEx(hkey, 'InstallPath')[0] winreg.CloseKey(hkey) except Exception: # FileNotFoundError, WindowsError, etc return None if sys.version_info[0] == 2: r_home = r_home.encode(sys.getfilesystemencoding()) return r_home def get_rlib_path(r_home: str, system: str) -> str: """Get the path for the R shared library.""" if system == 'Linux': lib_path = os.path.join(r_home, 'lib', 'libR.so') elif system == 'Darwin': lib_path = os.path.join(r_home, 'lib', 'libR.dylib') else: raise ValueError( 'The system {system} is currently not supported.' .format(system=system) ) return lib_path def get_r_home() -> Optional[str]: """Get R's home directory (aka R_HOME). If an environment variable R_HOME is found it is returned, and if none is found it is trying to get it from an R executable in the PATH. On Windows, a third last attempt is made by trying to obtain R_HOME from the registry. If all attempt are unfruitful, None is returned. """ r_home = os.environ.get('R_HOME') if not r_home: r_home = r_home_from_subprocess() if not r_home and sys.platform == 'win32': r_home = r_home_from_registry() return r_home def get_r_exec(r_home: str) -> str: """Get the path of the R executable/binary. :param: R HOME directory :return: Path to the R executable/binary""" if sys.platform == 'win32' and '64 bit' in sys.version: r_exec = os.path.join(r_home, 'bin', 'x64', 'R') else: r_exec = os.path.join(r_home, 'bin', 'R') return r_exec def _get_r_cmd_config(r_home: str, about: str, allow_empty=False): """Get the output of calling 'R CMD CONFIG '. :param r_home: R HOME directory :param about: argument passed to the command line 'R CMD CONFIG' :param allow_empty: allow the output to be empty :return: a tuple (lines of output)""" r_exec = get_r_exec(r_home) cmd = (r_exec, 'CMD', 'config', about) output = subprocess.check_output( cmd, universal_newlines=True ).split(os.linesep) # Twist if 'R RHOME' spits out a warning if output[0].startswith('WARNING'): warnings.warn('R emitting a warning: %s' % output[0]) res = output[1:] else: res = output return res _R_LIBS = ('LAPACK_LIBS', 'BLAS_LIBS') _R_FLAGS = ('--ldflags', '--cppflags') def get_r_flags(r_home: str, flags: str): """Get the parsed output of calling 'R CMD CONFIG '. Returns a tuple (parsed_args, unknown_args), with parsed_args having the attribute `l`, 'L', and 'I'.""" assert flags in _R_FLAGS parser = argparse.ArgumentParser() parser.add_argument('-I', action='append') parser.add_argument('-L', action='append') parser.add_argument('-l', action='append') res = shlex.split( ' '.join( _get_r_cmd_config(r_home, flags, allow_empty=False))) return parser.parse_known_args(res) def get_r_libs(r_home: str, libs: str): return _get_r_cmd_config(r_home, libs, allow_empty=True) class CExtensionOptions(object): """Options to compile C extensions.""" def __init__(self): self.extra_link_args = [] self.extra_compile_args = [] self.include_dirs = [] self.libraries = [] self.library_dirs = [] def add_include(self, args, unknown): """Add include directories. :param args: args as returned by get_r_flags(). :param unknown: unknown arguments a returned by get_r_flags().""" if args.I is None: warnings.warn('No include specified') else: self.include_dirs.extend(args.I) self.extra_compile_args.extend(unknown) def add_lib(self, args, unknown, ignore=('R', )): """Add libraries. :param args: args as returned by get_r_flags(). :param unknown: unknown arguments a returned by get_r_flags().""" if args.L is None: if args.l is None: # hmmm... no libraries at all warnings.warn('No libraries as -l arguments to the compiler.') else: self.libraries.extend([x for x in args.l if x not in ignore]) else: self.library_dirs.extend(args.L) self.libraries.extend(args.l) self.extra_link_args.extend(unknown) def _make_bold(text): return '%s%s%s' % ('\033[1m', text, '\033[0m') def iter_info(): yield _make_bold('rpy2 version:') if has_rpy2: # TODO: the repeated import is needed, without which Python (3.6) # raises an UnboundLocalError (local variable reference before # assignment). import rpy2 # noqa: F811 yield rpy2.__version__ else: yield 'rpy2 cannot be imported' yield _make_bold('Python version:') yield sys.version if not (sys.version_info[0] == 3 and sys.version_info[1] >= 5): yield '*** rpy2 is primarily designed for Python >= 3.5' yield _make_bold("Looking for R's HOME:") r_home = os.environ.get('R_HOME') yield ' Environment variable R_HOME: %s' % r_home if sys.platform in ('win32', 'nt'): r_home_default = r_home_from_registry() yield ' InstallPath in the registry: %s' % r_home else: r_home_default = r_home_from_subprocess() yield ' Calling `R RHOME`: %s' % r_home if r_home is not None and r_home_default is not None: if os.path.abspath(r_home) != r_home_default: yield (' Warning: The environment variable R_HOME ' 'differs from the default R in the PATH.') else: if r_home_default is None: yield (' Warning: There is no R in the PATH and no ' 'R_HOME defined.') else: r_home = r_home_default if has_rpy2: try: import rpy2.rinterface_lib.openrlib rlib_status = 'OK' except ImportError as ie: rlib_status = '*** Error while loading: %s ***' % str(ie) else: rlib_status = '*** rpy2 is not installed' yield _make_bold("R version:") yield ' In the PATH: %s' % r_version_from_subprocess() yield ' Loading R library from rpy2: %s' % rlib_status r_libs = os.environ.get('R_LIBS') yield _make_bold('Additional directories to load R packages from:') yield r_libs yield _make_bold('C extension compilation:') c_ext = CExtensionOptions() if r_home is None: yield (' Warning: R cannot be found, so no compilation flags ' 'can be extracted.') else: c_ext.add_lib(*get_r_flags(r_home, '--ldflags')) c_ext.add_include(*get_r_flags(r_home, '--cppflags')) yield ' include:' yield ' %s' % c_ext.include_dirs yield ' libraries:' yield ' %s' % c_ext.libraries yield ' library_dirs:' yield ' %s' % c_ext.library_dirs yield ' extra_compile_args:' yield ' %s' % c_ext.extra_compile_args yield ' extra_link_args:' yield ' %s' % c_ext.extra_link_args if __name__ == '__main__': parser = argparse.ArgumentParser( 'Command-line tool to report the rpy2' 'environment and help diagnose issues') args = parser.parse_args() for row in iter_info(): print(row) rpy2-3.2.6/rpy2/rinterface_lib/0000755000175000017500000000000013615572523017441 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/rinterface_lib/ffi_proxy.py0000644000175000017500000000637113576515767022044 0ustar laurentlaurent00000000000000import enum class InterfaceType(enum.Enum): ABI = 1 API = 2 class SignatureDefinition(object): def __init__(self, name, rtype, arguments): self.name = name self.rtype = rtype self.arguments = arguments @property def callback_def(self): return '{} ({})'.format(self.rtype, ' ,'.join(self.arguments)) @property def extern_def(self): return '{} {}({})'.format( self.rtype, self.name, ' ,'.join(self.arguments) ) @property def extern_python_def(self): return 'extern "Python" {} {}({});'.format( self.rtype, self.name, ' ,'.join(self.arguments) ) def get_ffi_mode(_rinterface_cffi): if hasattr(_rinterface_cffi, 'lib'): return InterfaceType.API else: return InterfaceType.ABI def callback(definition, _rinterface_cffi): def decorator(func): if get_ffi_mode(_rinterface_cffi) == InterfaceType.ABI: res = _rinterface_cffi.ffi.callback(definition.callback_def)(func) elif get_ffi_mode(_rinterface_cffi) == InterfaceType.API: res = _rinterface_cffi.ffi.def_extern()(func) else: raise RuntimeError('The cffi mode is neither ABI or API.') return res return decorator # Callbacks _capsule_finalizer_def = SignatureDefinition('_capsule_finalizer', 'void', ('SEXP',)) _evaluate_in_r_def = SignatureDefinition('_evaluate_in_r', 'SEXP', ('SEXP args',)) _consoleflush_def = SignatureDefinition('_consoleflush', 'void', ('void', )) _consoleread_def = SignatureDefinition('_consoleread', 'int', ('char *', 'unsigned char *', 'int', 'int')) _consolereset_def = SignatureDefinition('_consolereset', 'void', ('void', )) _consolewrite_def = SignatureDefinition('_consolewrite', 'void', ('char *', 'int')) _consolewrite_ex_def = SignatureDefinition('_consolewrite_ex', 'void', ('char *', 'int', 'int')) _showmessage_def = SignatureDefinition('_showmessage', 'void', ('char *', )) _choosefile_def = SignatureDefinition('_choosefile', 'int', ('int', 'char *', 'int')) _cleanup_def = SignatureDefinition('_cleanup', 'void', ('SA_TYPE', 'int', 'int')) _showfiles_def = SignatureDefinition('_showfiles', 'int', ('int', 'const char **', 'const char **', 'const char *', 'Rboolean', 'const char *')) _processevents_def = SignatureDefinition('_processevents', 'void', ('void', )) _busy_def = SignatureDefinition('_busy', 'void', ('int', )) _callback_def = SignatureDefinition('_callback', 'void', ('void', )) # TODO: should be const char * _yesnocancel_def = SignatureDefinition('_yesnocancel', 'int', ('char *', )) _parsevector_wrap_def = SignatureDefinition('_parsevector_wrap', 'SEXP', ('void *data', )) _handler_def = SignatureDefinition('_handler_wrap', 'SEXP', ('SEXP cond', 'void *hdata')) rpy2-3.2.6/rpy2/rinterface_lib/bufferprotocol.py0000644000175000017500000000176213576515767023071 0ustar laurentlaurent00000000000000from . import openrlib def getrank(cdata): dim_cdata = openrlib.rlib.Rf_getAttrib(cdata, openrlib.rlib.R_DimSymbol) if dim_cdata == openrlib.rlib.R_NilValue: return 1 else: return openrlib.rlib.Rf_length(dim_cdata) def getshape(cdata, rk=None) -> tuple: if rk is None: rk = getrank(cdata) dim_cdata = openrlib.rlib.Rf_getAttrib(cdata, openrlib.rlib.R_DimSymbol) shape = [None, ] * rk if dim_cdata == openrlib.rlib.R_NilValue: shape[0] = openrlib.rlib.Rf_length(cdata) else: for i in range(rk): shape[i] = openrlib.INTEGER_ELT(dim_cdata, i) return tuple(shape) def getstrides(cdata, shape, itemsize): rk = len(shape) strides = [None, ] * rk strides[0] = itemsize for i in range(1, rk): strides[i] = shape[i-1] * strides[i-1] return tuple(strides) def getbuffer(cdata): raise NotImplementedError() rpy2-3.2.6/rpy2/rinterface_lib/_rinterface_capi.py0000644000175000017500000004014013615570032023260 0ustar laurentlaurent00000000000000# TODO: make it cffi-buildable with conditional function definition # (Python if ABI, C if API) import enum import logging import warnings from . import ffi_proxy from . import openrlib from . import conversion from . import embedded from . import memorymanagement logger = logging.getLogger(__name__) ffi = openrlib.ffi # TODO: How can I reliably get MAX_INT from limits.h ? _MAX_INT = 2**32-1 _R_PRESERVED = dict() _PY_PASSENGER = dict() FFI_MODE = ffi_proxy.get_ffi_mode(openrlib._rinterface_cffi) def get_rid(cdata) -> int: """Get the identifier for the R object. This is intended to be like Python's `id()`, but for R objects mapped by rpy2.""" return int(ffi.cast('uintptr_t', cdata)) def protected_rids(): """Sequence of R IDs protected from collection by rpy2.""" keys = tuple(_R_PRESERVED.keys()) res = [] for k in keys: v = _R_PRESERVED.get(k) if v: res.append((get_rid(k), v)) return tuple(res) def is_cdata_sexp(obj) -> bool: """Is the object a cffi `CData` object pointing to an R object ?""" if (isinstance(obj, ffi.CData) and ffi.typeof(obj).cname == 'struct SEXPREC *'): return True else: return False def _preserve(cdata): addr = int(ffi.cast('uintptr_t', cdata)) count = _R_PRESERVED.get(addr, 0) if count == 0: openrlib.rlib.R_PreserveObject(cdata) _R_PRESERVED[addr] = count + 1 return addr def _release(cdata): addr = int(ffi.cast('uintptr_t', cdata)) count = _R_PRESERVED[addr] - 1 if count == 0: del(_R_PRESERVED[addr]) openrlib.rlib.R_ReleaseObject(cdata) else: _R_PRESERVED[addr] = count @ffi_proxy.callback(ffi_proxy._capsule_finalizer_def, openrlib._rinterface_cffi) def _capsule_finalizer(cdata): try: openrlib.rlib.R_ClearExternalPtr(cdata) except Exception as e: warnings.warn('Exception downgraded to warning: %s' % str(e)) # ABI and API modes differs in the what is the exact callback object to be # passed to C code. if hasattr(openrlib._rinterface_cffi, 'lib'): _capsule_finalizer_c = openrlib._rinterface_cffi.lib._capsule_finalizer else: _capsule_finalizer_c = None class UnmanagedSexpCapsule(object): def __init__(self, cdata): assert is_cdata_sexp(cdata) self._cdata = cdata @property def typeof(self): return _TYPEOF(self._cdata) @property def rid(self) -> int: return get_rid(self._cdata) class SexpCapsule(UnmanagedSexpCapsule): __slots__ = ('_cdata', ) def __init__(self, cdata): assert is_cdata_sexp(cdata) _preserve(cdata) self._cdata = cdata def __del__(self): try: _release(self._cdata) except Exception as e: # _release can be None while capsules when Python is terminating # and R is being shutdown, resulting in a race condition when # freeing python objects. if _release is None: pass else: raise e class SexpCapsuleWithPassenger(SexpCapsule): __slots__ = ('_cdata', '_passenger') def __init__(self, cdata, passenger, ptr): assert is_cdata_sexp(cdata) addr = _preserve(cdata) _PY_PASSENGER[addr] = passenger self._cdata = cdata self._passenger = ptr def __del__(self): addr = get_rid(self._cdata) _release(self._cdata) if addr not in _PY_PASSENGER: del(_PY_PASSENGER[addr]) def _findvar(symbol, r_environment): rlib = openrlib.rlib return rlib.Rf_findVar(symbol, r_environment) def _findfun(symbol, r_environment): rlib = openrlib.rlib return rlib.Rf_findFun(symbol, r_environment) def _findVarInFrame(symbol, r_environment): return openrlib.rlib.Rf_findVarInFrame(r_environment, symbol) def _GET_DIM(robj): res = openrlib.rlib.Rf_getAttrib(robj, openrlib.rlib.R_DimSymbol) return res def _GET_DIMNAMES(robj): res = openrlib.rlib.Rf_getAttrib(robj, openrlib.rlib.R_DimNames) return res def _GET_LEVELS(robj): res = openrlib.rlib.Rf_getAttrib(robj, openrlib.rlib.R_LevelsSymbol) return res def _GET_NAMES(robj): res = openrlib.rlib.Rf_getAttrib(robj, openrlib.rlib.R_NamesSymbol) return res def _TYPEOF(robj): return robj.sxpinfo.type def _SET_TYPEOF(robj, v): robj.sxpinfo.type = v def _NAMED(robj): return robj.sxpinfo.named def _string_getitem(cdata, i): rlib = openrlib.rlib return conversion._cchar_to_str( rlib.R_CHAR(rlib.STRING_ELT(cdata, i)) ) # TODO: still used ? def _string_setitem(cdata, i, value_b): rlib = openrlib.rlib rlib.SET_STRING_ELT( cdata, i, rlib.Rf_mkCharCE(value_b, conversion._CE_DEFAULT_VALUE) ) def _has_slot(cdata, name_b): res = openrlib.rlib.R_has_slot(cdata, name_b) return bool(res) def build_rcall(rfunction, args=[], kwargs=[]): rlib = openrlib.rlib with memorymanagement.rmemory() as rmemory: rcall = rmemory.protect( rlib.Rf_allocList(len(args)+len(kwargs)+1) ) _SET_TYPEOF(rcall, rlib.LANGSXP) rlib.SETCAR(rcall, rfunction) item = rlib.CDR(rcall) for val in args: cdata = rmemory.protect(conversion._get_cdata(val)) rlib.SETCAR(item, cdata) item = rlib.CDR(item) for key, val in kwargs: if key is not None: _assert_valid_slotname(key) rlib.SET_TAG( item, rlib.Rf_install(conversion._str_to_cchar(key))) cdata = rmemory.protect(conversion._get_cdata(val)) rlib.SETCAR(item, cdata) item = rlib.CDR(item) return rcall def _evaluated_promise(function): def _(*args, **kwargs): robj = function(*args, **kwargs) if _TYPEOF(robj) == openrlib.rlib.PROMSXP: robj = openrlib.rlib.Rf_eval( robj, openrlib.rlib.R_GlobalEnv) return robj return _ def _python_index_to_c(cdata, i: int) -> int: """Compute the C value for a Python-style index. The function will translate a Python-style index that can be either positive or negative, if the later to indicate that indexing is relative to the end of the sequence, into a strictly positive or null index as use in C. Raises an exception IndexError if out of bounds.""" size = openrlib.rlib.Rf_xlength(cdata) if i < 0: # x = [0,1,2,3] # x[-3] = x[size + (-3)] = x[4 - 3] = x[1] = 1 # x[-2] = x[size + (-2)] = x[4 - 2] = x[2] = 2 i = size + i if i >= size or i < 0: raise IndexError('index out of range') return i # TODO: make it a general check for names or symbols ? def _assert_valid_slotname(name: str): if not isinstance(name, str): raise ValueError('Invalid name %s' % repr(name)) elif len(name) == 0: raise ValueError('The name cannot be an empty string') def _list_attrs(cdata): rlib = openrlib.rlib attrs = rlib.ATTRIB(cdata) nvalues = rlib.Rf_length(attrs) if rlib.Rf_isList(cdata): namesattr = rlib.Rf_getAttrib(cdata, rlib.R_NamesSymbol) if namesattr != rlib.R_NilValue: nvalues += 1 else: namesattr = rlib.R_NilValue with memorymanagement.rmemory() as rmemory: names = rmemory.protect( rlib.Rf_allocVector(rlib.STRSXP, nvalues)) attr_i = 0 if namesattr != rlib.R_NilValue: rlib.SET_STRING_ELT(names, attr_i, rlib.PRINTNAME(rlib.R_NamesSymbol)) attr_i += 1 while attrs != rlib.R_NilValue: tag = rlib.TAG(attrs) if _TYPEOF(tag) == rlib.SYMSXP: rlib.SET_STRING_ELT(names, attr_i, rlib.PRINTNAME(tag)) else: rlib.SET_STRING_ELT( names, attr_i, rlib.R_BlankString ) attrs = rlib.CDR(attrs) attr_i += 1 return names def _remove(name, env, inherits): rlib = openrlib.rlib with memorymanagement.rmemory() as rmemory: internal = rmemory.protect( rlib.Rf_install(conversion._str_to_cchar('.Internal'))) remove = rmemory.protect( rlib.Rf_install(conversion._str_to_cchar('remove'))) args = rmemory.protect(rlib.Rf_lang4(remove, name, env, inherits)) call = rmemory.protect(rlib.Rf_lang2(internal, args)) # TODO: use tryEval instead and catch errors. res = rlib.Rf_eval(call, rlib.R_GlobalEnv) return res def _geterrmessage(): rlib = openrlib.rlib # TODO: use isString and installTrChar with memorymanagement.rmemory() as rmemory: symbol = rmemory.protect( rlib.Rf_install( conversion._str_to_cchar('geterrmessage')) ) geterrmessage = _findvar( symbol, rlib.R_GlobalEnv) call_r = rlib.Rf_lang1(geterrmessage) res = rmemory.protect( rlib.Rf_eval(call_r, rlib.R_GlobalEnv) ) res = _string_getitem(res, 0) return res def serialize(cdata, cdata_env): """Serialize an R object to an R string. Note that the R string returned is *not* protected from the R garbage collection.""" rlib = openrlib.rlib with memorymanagement.rmemory() as rmemory: sym_serialize = rmemory.protect( rlib.Rf_install(conversion._str_to_cchar('serialize')) ) func_serialize = rmemory.protect( _findfun(sym_serialize, rlib.R_BaseEnv)) r_call = rmemory.protect( rlib.Rf_lang3(func_serialize, cdata, rlib.R_NilValue) ) error_occured = ffi.new('int *', 0) res = rlib.R_tryEval(r_call, cdata_env, error_occured) if error_occured[0]: raise embedded.RRuntimeError(_geterrmessage()) return res def unserialize(cdata, cdata_env): """Unserialize an R string to an R object. Note that the R object returned is *not* protected from the R garbage collection.""" rlib = openrlib.rlib with memorymanagement.rmemory() as rmemory: sym_unserialize = rmemory.protect( rlib.Rf_install( conversion._str_to_cchar('unserialize')) ) func_unserialize = rmemory.protect(_findfun(sym_unserialize, rlib.R_BaseEnv)) r_call = rmemory.protect( rlib.Rf_lang2(func_unserialize, cdata) ) error_occured = ffi.new('int *', 0) res = rlib.R_tryEval(r_call, cdata_env, error_occured) if error_occured[0]: raise embedded.RRuntimeError(_geterrmessage()) return res @ffi_proxy.callback(ffi_proxy._evaluate_in_r_def, openrlib._rinterface_cffi) def _evaluate_in_r(rargs): # 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 name = conversion._cchar_to_str( rlib.R_CHAR(rlib.PRINTNAME(rlib.TAG(rargs))) ) 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 _register_external_symbols() -> None: python_cchar = ffi.new('char []', b'.Python') ffi_proxy = openrlib.ffi_proxy if ( ffi_proxy.get_ffi_mode(openrlib._rinterface_cffi) == ffi_proxy.InterfaceType.ABI ): python_callback = ffi.cast('DL_FUNC', _evaluate_in_r) else: python_callback = ffi.cast('DL_FUNC', openrlib.rlib._evaluate_in_r) externalmethods = ffi.new( 'R_ExternalMethodDef[]', [[python_cchar, python_callback, -1], [ffi.NULL, ffi.NULL, 0]]) openrlib.rlib.R_registerRoutines( openrlib.rlib.R_getEmbeddingDllInfo(), ffi.NULL, ffi.NULL, ffi.NULL, externalmethods ) class PARSING_STATUS(enum.Enum): PARSE_NULL = openrlib.rlib.PARSE_NULL PARSE_OK = openrlib.rlib.PARSE_OK PARSE_INCOMPLETE = openrlib.rlib.PARSE_INCOMPLETE PARSE_ERROR = openrlib.rlib.PARSE_ERROR PARSE_EOF = openrlib.rlib.PARSE_EOF class RParsingError(Exception): def __init__(self, msg: str, status: int): super().__init__(msg) self.status = status @ffi_proxy.callback(ffi_proxy._parsevector_wrap_def, openrlib._rinterface_cffi) def _parsevector_wrap(data): try: cdata, num, status = ffi.from_handle(data) res = openrlib.rlib.R_ParseVector( cdata, # text num, status, openrlib.rlib.R_NilValue) except Exception as e: res = openrlib.rlib.R_NilValue logger.error('_parsevector_wrap: %s', str(e)) return res @ffi_proxy.callback(ffi_proxy._handler_def, openrlib._rinterface_cffi) def _handler_wrap(cond, hdata): return openrlib.rlib.R_NilValue if FFI_MODE is ffi_proxy.InterfaceType.ABI: _parsevector_wrap = _parsevector_wrap _handler_wrap = _handler_wrap elif FFI_MODE is ffi_proxy.InterfaceType.API: _parsevector_wrap = openrlib.rlib._parsevector_wrap _handler_wrap = openrlib.rlib._handler_wrap else: raise ImportError('cffi mode unknown: %s' % FFI_MODE) def _parse(cdata, num): status = ffi.new('ParseStatus[1]', None) data = ffi.new_handle((cdata, num, status)) hdata = ffi.NULL with memorymanagement.rmemory() as rmemory: res = rmemory.protect( openrlib.rlib.R_tryCatchError( _parsevector_wrap, data, _handler_wrap, hdata ) ) # TODO: design better handling of possible status: # PARSE_NULL, # PARSE_OK, # PARSE_INCOMPLETE, # PARSE_ERROR, # PARSE_EOF if status[0] != openrlib.rlib.PARSE_OK: raise RParsingError('Parsing status not OK', PARSING_STATUS(status[0])) return res rpy2-3.2.6/rpy2/rinterface_lib/memorymanagement.py0000644000175000017500000000304513576515767023377 0ustar laurentlaurent00000000000000"""Interface to and utilities for R's memory management.""" import contextlib from . import openrlib # TODO: make it extend ContextManager and delete the function # rmemory ? class ProtectionTracker(object): """Convenience class to keep track of R's C-level protection stack. Mixing this with direct calls to Rf_protect() and Rf_unprotect() in the C API from Python, or even using Rf_protect() and Rf_unprotect(), is strongly discouraged.""" def __init__(self): self._counter = 0 @property def count(self) -> int: """Return the count for the protection stack.""" return self._counter def protect(self, cdata): """Pass-through function that adds the R object to the short-term stack of objects protected from garbase collection.""" cdata = openrlib.rlib.Rf_protect(cdata) self._counter += 1 return cdata def unprotect(self, n: int) -> None: """Release the n objects last added to the protection stack.""" if n > self._counter: raise ValueError('n > count') self._counter -= n openrlib.rlib.Rf_unprotect(n) def unprotect_all(self) -> None: """Release the total count of objects this instance knows to be protected from the protection stack.""" openrlib.rlib.Rf_unprotect(self._counter) self._counter = 0 @contextlib.contextmanager def rmemory(): pt = ProtectionTracker() with openrlib.rlock: try: yield pt finally: pt.unprotect_all() rpy2-3.2.6/rpy2/rinterface_lib/openrlib.py0000644000175000017500000001037413602016462021621 0ustar laurentlaurent00000000000000import platform import threading import rpy2.situation from rpy2.rinterface_lib import ffi_proxy cffi_mode = rpy2.situation.get_cffi_mode() if cffi_mode == rpy2.situation.CFFI_MODE.API: import _rinterface_cffi_api as _rinterface_cffi elif cffi_mode == rpy2.situation.CFFI_MODE.ABI: import _rinterface_cffi_abi as _rinterface_cffi else: try: import _rinterface_cffi_api as _rinterface_cffi except ImportError: import _rinterface_cffi_abi as _rinterface_cffi ffi = _rinterface_cffi.ffi # TODO: Separate the functions in the module from the side-effect of # finding R_HOME and opening the shared library. R_HOME = rpy2.situation.get_r_home() rlock = threading.RLock() def _dlopen_rlib(r_home: str): """Open R's shared C library. This is only relevant in ABI mode.""" if r_home is None: raise ValueError('r_home is None. ' 'Try python -m rpy2.situation') lib_path = rpy2.situation.get_rlib_path(r_home, platform.system()) if lib_path is None: raise ValueError('The library path cannot be None.') else: rlib = ffi.dlopen(lib_path) return rlib if ffi_proxy.get_ffi_mode(_rinterface_cffi) == ffi_proxy.InterfaceType.API: rlib = _rinterface_cffi.lib else: rlib = _dlopen_rlib(R_HOME) # R macros and functions def _get_symbol_or_fallback(symbol: str, fallback): """Get a cffi object from rlib, or the fallback if missing.""" try: res = getattr(rlib, symbol) except (ffi.error, AttributeError): res = fallback return res def _get_dataptr_fallback(vec): # DATAPTR seems to be the following macro in R < 3.5 # but I cannot get it to work (seems to be pointing # to incorrect memory region). # (((SEXPREC_ALIGN *)(x)) + 1) raise NotImplementedError() DATAPTR = _get_symbol_or_fallback('DATAPTR', _get_dataptr_fallback) def _LOGICAL(x): return ffi.cast('int *', DATAPTR(x)) LOGICAL = rlib.LOGICAL def _INTEGER(x): return ffi.cast('int *', DATAPTR(x)) INTEGER = rlib.INTEGER def _RAW(x): return ffi.cast('Rbyte *', DATAPTR(x)) RAW = rlib.RAW def _REAL(robj): return ffi.cast('double *', DATAPTR(robj)) REAL = rlib.REAL def _COMPLEX(robj): return ffi.cast('Rcomplex *', DATAPTR(robj)) COMPLEX = rlib.COMPLEX def _get_raw_elt_fallback(vec, i: int): return RAW(vec)[i] RAW_ELT = _get_symbol_or_fallback('RAW_ELT', _get_raw_elt_fallback) def _get_integer_elt_fallback(vec, i: int): return INTEGER(vec)[i] INTEGER_ELT = _get_symbol_or_fallback('INTEGER_ELT', _get_integer_elt_fallback) def _set_integer_elt_fallback(vec, i: int, value): INTEGER(vec)[i] = value SET_INTEGER_ELT = _get_symbol_or_fallback('SET_INTEGER_ELT', _set_integer_elt_fallback) def _get_logical_elt_fallback(vec, i: int): return LOGICAL(vec)[i] LOGICAL_ELT = _get_symbol_or_fallback('LOGICAL_ELT', _get_logical_elt_fallback) def _set_logical_elt_fallback(vec, i: int, value): LOGICAL(vec)[i] = value SET_LOGICAL_ELT = _get_symbol_or_fallback('SET_LOGICAL_ELT', _set_logical_elt_fallback) def _get_real_elt_fallback(vec, i: int): return REAL(vec)[i] REAL_ELT = _get_symbol_or_fallback('REAL_ELT', _get_real_elt_fallback) def _set_real_elt_fallback(vec, i: int, value): REAL(vec)[i] = value SET_REAL_ELT = _get_symbol_or_fallback('SET_REAL_ELT', _set_real_elt_fallback) def _get_complex_elt_fallback(vec, i: int): return COMPLEX(vec)[i] COMPLEX_ELT = _get_symbol_or_fallback('COMPLEX_ELT', _get_complex_elt_fallback) def SET_COMPLEX_ELT(vec, i: int, value: complex): COMPLEX(vec)[i].r = value.real COMPLEX(vec)[i].i = value.imag # TODO: still useful or is it in the C API ? def _VECTOR_ELT(robj, i): return ffi.cast('SEXP *', DATAPTR(robj))[i] def _STRING_PTR(robj): return ffi.cast('SEXP *', DATAPTR(robj)) def _VECTOR_PTR(robj): return ffi.cast('SEXP *', DATAPTR(robj)) def _STRING_VALUE(robj): return rlib.R_CHAR(rlib.Rf_asChar(robj)) rpy2-3.2.6/rpy2/rinterface_lib/__init__.py0000644000175000017500000000000013576515767021555 0ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2/rinterface_lib/na_values.py0000644000175000017500000000507513576515767022014 0ustar laurentlaurent00000000000000"""NA (Non-Available) values in R.""" from . import embedded from . import openrlib from . import sexp from . import _rinterface_capi as _rinterface class Singleton(type): _instances = {} def __call__(cls, *args, **kwargs): instances = cls._instances if cls not in instances: instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return instances[cls] NA_Character = None NA_Integer = None NA_Logical = None NA_Real = None NA_Complex = None class NAIntegerType(int, metaclass=Singleton): def __new__(cls, *args, **kwargs): embedded.assert_isready() return super().__new__(cls, openrlib.rlib.R_NaInt) def __repr__(self) -> str: return 'NA_integer_' def __str__(self) -> str: return 'NA_integer_' def __bool__(self): raise ValueError('R value for missing integer value') class NACharacterType(sexp.CharSexp, metaclass=Singleton): def __init__(self): embedded.assert_isready() super().__init__( sexp.CharSexp( _rinterface.SexpCapsule(openrlib.rlib.R_NaString) ) ) def __repr__(self) -> str: return 'NA_character_' def __str__(self) -> str: return 'NA_character_' def __bool__(self): raise ValueError('R value for missing character value') class NALogicalType(int, metaclass=Singleton): def __new__(cls, *args, **kwargs): embedded.assert_isready() return super().__new__(cls, openrlib.rlib.R_NaInt) def __repr__(self) -> str: return 'NA' def __str__(self) -> str: return 'NA' def __bool__(self) -> bool: raise ValueError('R value for missing boolean value') class NARealType(float, metaclass=Singleton): def __new__(cls, *args, **kwargs): embedded.assert_isready() return super().__new__(cls, openrlib.rlib.R_NaReal) def __repr__(self) -> str: return 'NA_real_' def __str__(self) -> str: return 'NA_real_' def __bool__(self) -> bool: raise ValueError('R value for missing float value') class NAComplexType(complex, metaclass=Singleton): def __new__(cls, *args, **kwargs): embedded.assert_isready() return super().__new__(cls, openrlib.rlib.R_NaReal, openrlib.rlib.R_NaReal) def __repr__(self) -> str: return 'NA_complex_' def __str__(self) -> str: return 'NA_complex_' def __bool__(self): raise ValueError('R value for missing complex value') rpy2-3.2.6/rpy2/rinterface_lib/embedded.py0000644000175000017500000001434613615570032021545 0ustar laurentlaurent00000000000000import enum import os import sys import typing import warnings from . import openrlib from . import callbacks ffi = openrlib.ffi _options: typing.Tuple[str, ...] = ('rpy2', '--quiet', '--no-save') _C_stack_limit = -1 rpy2_embeddedR_isinitialized = 0x00 rstart = None # TODO: move initialization-related code to _rinterface ? class RPY_R_Status(enum.Enum): INITIALIZED = 0x01 BUSY = 0x02 ENDED = 0x04 def set_initoptions(options: typing.Tuple[str]) -> None: if rpy2_embeddedR_isinitialized: raise RuntimeError('Options can no longer be set once ' 'R is initialized.') global _options for x in options: assert isinstance(x, str) _options = tuple(options) def get_initoptions() -> typing.Tuple[str, ...]: return _options def isinitialized() -> bool: """Is the embedded R initialized.""" return bool(rpy2_embeddedR_isinitialized & RPY_R_Status.INITIALIZED.value) def setinitialized() -> None: global rpy2_embeddedR_isinitialized rpy2_embeddedR_isinitialized = RPY_R_Status.INITIALIZED.value def isready() -> bool: """Is the embedded R ready for use.""" INITIALIZED = RPY_R_Status.INITIALIZED return bool( rpy2_embeddedR_isinitialized == INITIALIZED.value ) def assert_isready() -> None: """Assert whether R is ready (initialized). Raises an RNotReadyError if it is not.""" if not isready(): raise RNotReadyError( 'The embedded R is not ready to use.') class RNotReadyError(Exception): """Embedded R is not ready to use.""" pass class RRuntimeError(Exception): """Error generated by R.""" pass def _setcallback(rlib, rlib_symbol, callbacks, callback_symbol) -> None: """Set R callbacks.""" if callback_symbol is None: new_callback = ffi.NULL else: new_callback = getattr(callbacks, callback_symbol) setattr(rlib, rlib_symbol, new_callback) CALLBACK_INIT_PAIRS = (('ptr_R_WriteConsoleEx', '_consolewrite_ex'), ('ptr_R_WriteConsole', None), ('ptr_R_ShowMessage', '_showmessage'), ('ptr_R_ReadConsole', '_consoleread'), ('ptr_R_FlushConsole', '_consoleflush'), ('ptr_R_ResetConsole', '_consolereset'), ('ptr_R_ChooseFile', '_choosefile'), ('ptr_R_ShowFiles', '_showfiles'), ('ptr_R_CleanUp', '_cleanup'), ('ptr_R_ProcessEvents', '_processevents'), ('ptr_R_Busy', '_busy')) # TODO: can init_once() be used here ? def _initr(interactive: bool = True, _want_setcallbacks: bool = True) -> typing.Optional[int]: rlib = openrlib.rlib ffi_proxy = openrlib.ffi_proxy if ( ffi_proxy.get_ffi_mode(openrlib._rinterface_cffi) == ffi_proxy.InterfaceType.ABI ): callback_funcs = callbacks else: callback_funcs = rlib with openrlib.rlock: if isinitialized(): return None os.environ['R_HOME'] = openrlib.R_HOME options_c = [ffi.new('char[]', o.encode('ASCII')) for o in _options] n_options = len(options_c) n_options_c = ffi.cast('int', n_options) status = rlib.Rf_initialize_R(n_options_c, options_c) setinitialized() # global rstart # rstart = ffi.new('Rstart') # rstart.R_Interactive = interactive # TODO: Conditional definition in C code # (Aqua, TERM, and TERM not "dumb") rlib.R_Outputfile = ffi.NULL rlib.R_Consolefile = ffi.NULL # TODO: Conditional in C code rlib.R_SignalHandlers = 0 if _want_setcallbacks: for rlib_symbol, callback_symbol in CALLBACK_INIT_PAIRS: _setcallback(rlib, rlib_symbol, callback_funcs, callback_symbol) # TODO: still needed ? rlib.R_CStackLimit = ffi.cast('uintptr_t', _C_stack_limit) rlib.setup_Rmainloop() return status def endr(fatal: int) -> None: global rpy2_embeddedR_isinitialized rlib = openrlib.rlib with openrlib.rlock: if rpy2_embeddedR_isinitialized & RPY_R_Status.ENDED.value: return rlib.R_dot_Last() rlib.R_RunExitFinalizers() rlib.Rf_KillAllDevices() rlib.R_CleanTempDir() rlib.R_gc() rlib.Rf_endEmbeddedR(fatal) rpy2_embeddedR_isinitialized ^= RPY_R_Status.ENDED.value _REFERENCE_TO_R_SESSIONS = 'https://github.com/rstudio/reticulate/issues/98' _R_SESSION_INITIALIZED = 'R_SESSION_INITIALIZED' _PYTHON_SESSION_INITIALIZED = 'PYTHON_SESSION_INITIALIZED' def get_r_session_status(r_session_init=None) -> dict: """Return information about the R session, if available. Information about the R session being already initialized can be communicated by an environment variable exported by the process that initialized it. See discussion at: %s """ % _REFERENCE_TO_R_SESSIONS res = {'current_pid': os.getpid()} if r_session_init is None: r_session_init = os.environ.get(_R_SESSION_INITIALIZED) if r_session_init: for item in r_session_init.split(':'): try: key, value = item.split('=', 1) except ValueError: warnings.warn( 'The item %s in %s should be of the form key=value.' % (item, _R_SESSION_INITIALIZED) ) res[key] = value return res def is_r_externally_initialized() -> bool: r_status = get_r_session_status() return str(r_status['current_pid']) == str(r_status.get('PID')) def set_python_process_info() -> None: """Set information about the Python process in an environment variable. Current the information See discussion at: %s """ % _REFERENCE_TO_R_SESSIONS info = (('current_pid', os.getpid()), ('sys.executable', sys.executable)) info_string = ':'.join('%s=%s' % x for x in info) os.environ[_PYTHON_SESSION_INITIALIZED] = info_string # R environments, initialized with rpy2.rinterface.SexpEnvironment # objects when R is initialized. emptyenv = None baseenv = None globalenv = None rpy2-3.2.6/rpy2/rinterface_lib/conversion.py0000644000175000017500000001035213615570032022172 0ustar laurentlaurent00000000000000"""Conversion between Python objects, C objects, and R objects.""" # TODO: rename the module with a prefix _ to indicate that this should # not be used outside of rpy2's own code from typing import Dict from typing import Type from . import openrlib from . import _rinterface_capi as _rinterface ffi = openrlib.ffi _R_RPY2_MAP: Dict[int, Type] = {} class DummyMissingRpy2Map(object): def __init__(self, *args, **kwargs): raise NotImplementedError('The default object mapper class is no set.') _R_RPY2_DEFAULT_MAP = DummyMissingRpy2Map # TODO: shouldn't the second type strictly inherit from an rpy2 # R object ? _PY_RPY2_MAP: Dict[Type, Type] = {} def _cdata_to_rinterface(cdata): scaps = _rinterface.SexpCapsule(cdata) t = cdata.sxpinfo.type if t in _R_RPY2_MAP: cls = _R_RPY2_MAP[t] else: cls = _R_RPY2_DEFAULT_MAP return cls(scaps) def _cdata_res_to_rinterface(function): def _(*args, **kwargs): cdata = function(*args, **kwargs) # TODO: test cdata is of the expected CType return _cdata_to_rinterface(cdata) return _ def _sexpcapsule_to_rinterface(scaps: '_rinterface.SexpCapsule'): cls = _R_RPY2_MAP.get(scaps.typeof, _R_RPY2_DEFAULT_MAP) return cls(scaps) # TODO: The name of the function is misleading, I think. Consider changing it. def _python_to_cdata(obj): t = type(obj) if t in _PY_RPY2_MAP: cls = _PY_RPY2_MAP[t] else: raise ValueError(obj) # cls = _PY_RPY2_DEFAULT_MAP cdata = cls(obj) return cdata # TODO: can scalars in R's C API be used ? def _int_to_sexp(val: int): rlib = openrlib.rlib # TODO: test value is not too large for R's ints s = rlib.Rf_protect(rlib.Rf_allocVector(rlib.INTSXP, 1)) openrlib.SET_INTEGER_ELT(s, 0, val) rlib.Rf_unprotect(1) return s def _bool_to_sexp(val: bool): # TODO: test value is not too large for R's ints rlib = openrlib.rlib s = rlib.Rf_protect(rlib.Rf_allocVector(rlib.LGLSXP, 1)) openrlib.SET_LOGICAL_ELT(s, 0, int(val)) rlib.Rf_unprotect(1) return s def _float_to_sexp(val: float): rlib = openrlib.rlib s = rlib.Rf_protect(rlib.Rf_allocVector(rlib.REALSXP, 1)) openrlib.SET_REAL_ELT(s, 0, val) rlib.Rf_unprotect(1) return s def _complex_to_sexp(val: complex): rlib = openrlib.rlib s = rlib.Rf_protect(rlib.Rf_allocVector(rlib.CPLXSXP, 1)) openrlib.SET_COMPLEX_ELT( s, 0, val ) rlib.Rf_unprotect(1) return s # Default encoding for converting R string back to Python # As defined in R_API.h, possible values are # CE_NATIVE = 0, # CE_UTF8 = 1, # CE_LATIN1 = 2, # CE_BYTES = 3, # CE_SYMBOL = 5, # CE_ANY = 99 _CE_DEFAULT_VALUE = openrlib.rlib.CE_UTF8 def _str_to_cchar(s: str, encoding: str = 'utf-8'): # TODO: use isStrinb and installTrChar b = s.encode(encoding) return ffi.new('char[]', b) def _cchar_to_str(c, encoding: str = 'utf-8') -> str: # TODO: use isStrinb and installTrChar s = ffi.string(c).decode(encoding) return s def _cchar_to_str_with_maxlen(c, maxlen: int) -> str: # TODO: use isStrinb and installTrChar s = ffi.string(c, maxlen).decode('utf-8') return s def _str_to_charsxp(val: str): rlib = openrlib.rlib if val is None: s = rlib.R_NaString else: cchar = _str_to_cchar(val) s = rlib.Rf_mkCharCE(cchar, _CE_DEFAULT_VALUE) return s def _str_to_sexp(val: str): rlib = openrlib.rlib s = rlib.Rf_protect(rlib.Rf_allocVector(rlib.STRSXP, 1)) charval = _str_to_charsxp(val) rlib.SET_STRING_ELT(s, 0, charval) rlib.Rf_unprotect(1) return s def _str_to_symsxp(val: str): rlib = openrlib.rlib cchar = _str_to_cchar(val) s = rlib.Rf_install(cchar) return s _PY_R_MAP = {} # TODO: Do special values such as NAs need to be cast into a SEXP when # a scalar ? def _get_cdata(obj): cast = _PY_R_MAP.get(type(obj)) if cast is False: cdata = obj elif cast is None: try: cdata = obj.__sexp__._cdata except AttributeError: raise ValueError('Not an rpy2 R object and unable ' 'to cast it into one: %s' % repr(obj)) else: cdata = cast(obj) return cdata rpy2-3.2.6/rpy2/rinterface_lib/R_API.h0000644000175000017500000003024713615570032020503 0ustar laurentlaurent00000000000000typedef unsigned int SEXPTYPE; const unsigned int NILSXP = 0; const unsigned int SYMSXP = 1; const unsigned int LISTSXP = 2; const unsigned int CLOSXP = 3; const unsigned int ENVSXP = 4; const unsigned int PROMSXP = 5; const unsigned int LANGSXP = 6; const unsigned int SPECIALSXP = 7; const unsigned int BUILTINSXP = 8; const unsigned int CHARSXP = 9; const unsigned int LGLSXP = 10; const unsigned int INTSXP = 13; const unsigned int REALSXP = 14; const unsigned int CPLXSXP = 15; const unsigned int STRSXP = 16; const unsigned int DOTSXP = 17; const unsigned int ANYSXP = 18; const unsigned int VECSXP = 19; const unsigned int EXPRSXP = 20; const unsigned int BCODESXP = 21; const unsigned int EXTPTRSXP = 22; const unsigned int WEAKREFSXP = 23; const unsigned int RAWSXP = 24; const unsigned int S4SXP = 25; const unsigned int NEWSXP = 30; const unsigned int FREESXP = 31; const unsigned int FUNSXP = 99; /* include/R_exts/Complex.h */ typedef struct { double r; double i; } Rcomplex; /* include/Rinternals.h */ typedef int R_len_t; #ifdef RPY2_RLEN_LONG typedef ptrdiff_t R_xlen_t; #endif #ifdef RPY2_RLEN_SHORT typedef int R_xlen_t; #endif /* R_ext/Arith.h */ extern double R_NaN; /* IEEE NaN */ extern double R_PosInf; /* IEEE Inf */ extern double R_NegInf; /* IEEE -Inf */ extern double R_NaReal; /* NA_REAL: IEEE */ extern int R_NaInt; /* NA_INTEGER:= INT_MIN currently */ typedef unsigned char Rbyte; struct symsxp_struct { struct SEXPREC *pname; struct SEXPREC *value; struct SEXPREC *internal; }; struct listsxp_struct { struct SEXPREC *carval; struct SEXPREC *cdrval; struct SEXPREC *tagval; }; struct envsxp_struct { struct SEXPREC *frame; struct SEXPREC *enclos; struct SEXPREC *hashtab; }; struct closxp_struct { struct SEXPREC *formals; struct SEXPREC *body; struct SEXPREC *env; }; struct promsxp_struct { struct SEXPREC *value; struct SEXPREC *expr; struct SEXPREC *env; }; typedef struct SEXPREC *SEXP; struct sxpinfo_struct { SEXPTYPE type : 5; unsigned int scalar: 1; unsigned int alt : 1; unsigned int obj : 1; unsigned int gp : 16; unsigned int mark : 1; unsigned int debug : 1; unsigned int trace : 1; unsigned int spare : 1; unsigned int gcgen : 1; unsigned int gccls : 3; unsigned int named : 16; unsigned int extra : 32; }; struct primsxp_struct { int offset; }; struct vecsxp_struct { R_xlen_t length; R_xlen_t truelength; }; typedef struct SEXPREC { /* defined as a macro SEXPREC_HEADER in R headers */ struct sxpinfo_struct sxpinfo; struct SEXPREC *attrib; struct SEXPREC *gengc_next_node, *gengc_prev_node; /* --- */ union { struct primsxp_struct primsxp; struct symsxp_struct symsxp; struct listsxp_struct listsxp; struct envsxp_struct envsxp; struct closxp_struct closxp; struct promsxp_struct promsxp; } u; } SEXPREC; typedef struct { /* defined as a macro SEXPREC_HEADER in R headers */ struct sxpinfo_struct sxpinfo; struct SEXPREC *attrib; struct SEXPREC *gengc_next_node, *gengc_prev_node; /* --- */ struct vecsxp_struct vecsxp; } VECTOR_SEXPREC, *VECSEXP; typedef union { VECTOR_SEXPREC s; double align; } SEXPREC_ALIGN; const char* R_CHAR(SEXP x); /* include/R_ext/Boolean.h */ typedef enum { FALSE = 0, TRUE } Rboolean; /* include/Rembedded.h */ int Rf_initialize_R(int ac, char **av); extern void Rf_initEmbeddedR(int argc, char *argv[]); extern void R_RunExitFinalizers(void); extern void Rf_KillAllDevices(void); extern void R_CleanTempDir(void); extern void Rf_endEmbeddedR(int fatal); /* include/R_ext/Memory.h */ void R_gc(void); /* include/Rinternals.h */ void R_ClearExternalPtr(SEXP s); void R_dot_Last(void); /* src/include/Rinlinedfunc.h */ void* DATAPTR(SEXP x); /* include/Rinternals.h */ SEXP (TAG)(SEXP e); SEXP SET_TAG(SEXP x, SEXP y); SEXP (CDR)(SEXP e); SEXP SETCDR(SEXP x, SEXP y); SEXP Rf_nthcdr(SEXP, int); SEXP (CAR)(SEXP e); SEXP SETCAR(SEXP x, SEXP y); SEXP Rf_getAttrib(SEXP sexp, SEXP what); SEXP Rf_setAttrib(SEXP x, SEXP what, SEXP n); SEXP Rf_namesgets(SEXP, SEXP); int R_has_slot(SEXP sexp, SEXP name); SEXP R_do_slot(SEXP sexp, SEXP name); SEXP R_do_slot_assign(SEXP sexp, SEXP name, SEXP value); SEXP (ATTRIB)(SEXP x); SEXP Rf_asChar(SEXP sexp); SEXP Rf_allocList(int n); SEXP Rf_allocVector(SEXPTYPE sexp_t, R_xlen_t n); SEXP Rf_elt(SEXP, int); typedef void (*R_CFinalizer_t)(SEXP); void R_RegisterCFinalizer(SEXP s, R_CFinalizer_t fun); typedef void* (*DL_FUNC)(); SEXP R_MakeExternalPtr(DL_FUNC p, SEXP tag, SEXP prot); SEXP Rf_lang1(SEXP); SEXP Rf_lang2(SEXP, SEXP); SEXP Rf_lang3(SEXP, SEXP, SEXP); SEXP Rf_lang4(SEXP, SEXP, SEXP, SEXP); SEXP Rf_lang5(SEXP, SEXP, SEXP, SEXP, SEXP); SEXP Rf_lang6(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP); R_len_t Rf_length(SEXP x); SEXP Rf_ScalarComplex(Rcomplex c); SEXP Rf_ScalarInteger(int n); SEXP Rf_ScalarLogical(int n); SEXP Rf_ScalarRaw(Rbyte b); SEXP Rf_ScalarReal(double f); SEXP Rf_ScalarString(SEXP s); void *(STDVEC_DATAPTR)(SEXP x); /* Integer.*/ int (INTEGER_ELT)(SEXP x, R_xlen_t i); void SET_INTEGER_ELT(SEXP x, R_xlen_t i, int v); int *(INTEGER)(SEXP x); /* Double / real. */ double (REAL_ELT)(SEXP x, R_xlen_t i); void SET_REAL_ELT(SEXP x, R_xlen_t i, double v); double *(REAL)(SEXP x); /* Boolean / logical. */ int (LOGICAL_ELT)(SEXP x, R_xlen_t i); void SET_LOGICAL_ELT(SEXP x, R_xlen_t i, int v); int *(LOGICAL)(SEXP x); /* Complex. */ Rcomplex (COMPLEX_ELT)(SEXP x, R_xlen_t i); Rcomplex *(COMPLEX)(SEXP x); /* Bytes / raw. */ Rbyte *(RAW)(SEXP x); Rbyte (RAW_ELT)(SEXP x, R_xlen_t i); SEXP (STRING_ELT)(SEXP x, R_xlen_t i); void SET_STRING_ELT(SEXP x, R_xlen_t i, SEXP v); SEXP (VECTOR_ELT)(SEXP x, R_xlen_t i); SEXP SET_VECTOR_ELT(SEXP x, R_xlen_t i, SEXP v); SEXP (CLOENV)(SEXP x); SEXP Rf_eval(SEXP, SEXP); SEXP R_tryEval(SEXP, SEXP, int*); SEXP R_tryCatchError(SEXP (*fun)(void *data), void *data, SEXP (*hndlr)(SEXP cond, void *hdata), void *hdata); SEXP Rf_findFun(SEXP sym, SEXP env); // SEXP Rf_findFun3(SEXP, SEXP, SEXP); SEXP Rf_findVar(SEXP sym, SEXP env); SEXP Rf_findVarInFrame(SEXP env, SEXP sym); SEXP Rf_findVarInFrame3(SEXP, SEXP, Rboolean); R_xlen_t Rf_xlength(SEXP); SEXP R_lsInternal(SEXP, Rboolean); SEXP Rf_duplicate(SEXP s); SEXP Rf_defineVar(SEXP sym, SEXP s, SEXP env); /* src/include/Rinlinedfunc.h */ SEXP Rf_protect(SEXP s); void Rf_unprotect(int n); void R_PreserveObject(SEXP s); void R_ReleaseObject(SEXP s); extern SEXP R_NaString; /* a CHARSXP */ extern SEXP R_BlankString; extern SEXP R_BlankScalarString; extern SEXP R_GlobalEnv; extern SEXP R_EmptyEnv; Rboolean R_EnvironmentIsLocked(SEXP env); extern SEXP R_BaseEnv; extern SEXP R_BaseNamespace; extern SEXP R_NilValue; extern SEXP R_UnboundValue; extern SEXP R_MissingArg; Rboolean (Rf_isNull)(SEXP s); Rboolean (Rf_isList)(SEXP s); SEXP Rf_install(const char *); SEXP Rf_installChar(SEXP x); SEXP Rf_mkChar(const char *); SEXP Rf_mkString(const char *); typedef enum { CE_NATIVE = 0, CE_UTF8 = 1, CE_LATIN1 = 2, CE_BYTES = 3, CE_SYMBOL = 5, CE_ANY = 99 } cetype_t; cetype_t Rf_getCharCE(SEXP); SEXP Rf_mkCharCE(const char *, cetype_t); SEXP Rf_mkCharLenCE(const char *, int n, cetype_t encoding); typedef enum { Bytes = 0, Chars = 1, Width = 2 } nchar_type; int R_nchar(SEXP string, nchar_type type_, Rboolean allowNA, Rboolean keepNA, const char* msg_name); SEXP (PRINTNAME)(SEXP x); SEXP (FRAME)(SEXP x); SEXP (ENCLOS)(SEXP x); SEXP (HASHTAB)(SEXP x); int (ENVFLAGS)(SEXP x); void (SET_ENVFLAGS)(SEXP x, int v); /* TODO: Why isn't this working ? void SET_FRAME(SEXP x, SEXP, v); void SET_ENCLOS(SEXP x, SEXP, v); void SET_HASHTAB(SEXP x, SEXP, v); */ /* include/Rdefines.h */ extern SEXP R_ClassSymbol; extern SEXP R_NameSymbol; extern SEXP R_DimSymbol; /* include/R_ext/Parse.h */ typedef enum { PARSE_NULL, PARSE_OK, PARSE_INCOMPLETE, PARSE_ERROR, PARSE_EOF } ParseStatus; SEXP R_ParseVector(SEXP text, int num, ParseStatus *status, SEXP srcfile); /* include/Rinterface.h */ extern Rboolean R_Interactive ; extern int R_SignalHandlers; extern uintptr_t R_CStackLimit; typedef enum { SA_NORESTORE,/* = 0 */ SA_RESTORE, SA_DEFAULT,/* was === SA_RESTORE */ SA_NOSAVE, SA_SAVE, SA_SAVEASK, SA_SUICIDE } SA_TYPE; #ifdef OSNAME_NT char *getDLLVersion(void); char *getRUser(void); char *get_R_HOME(void); void setup_term_ui(void); int UserBreak; Rboolean AllDevicesKilled; void editorcleanall(void); int GA_initapp(int, char **); void GA_appcleanup(void); void readconsolecfg(void); typedef int (*blah1) (const char *, char *, int, int); typedef void (*blah2) (const char *, int); typedef void (*blah3) (void); typedef void (*blah4) (const char *); /* Return value here is expected to be 1 for Yes, -1 for No and * 0 for Cancel: symbolic constants in graphapp.h */ typedef int (*blah5) (const char *); typedef void (*blah6) (int); typedef void (*blah7) (const char *, int, int); typedef enum {RGui, RTerm, LinkDLL} UImode; #endif /* preprocess-define-begin */ #define R_SIZE_T size_t /* preprocess-define-end */ typedef struct { Rboolean R_Quiet; Rboolean R_Slave; Rboolean R_Interactive; Rboolean R_Verbose; Rboolean LoadSiteFile; Rboolean LoadInitFile; Rboolean DebugInitFile; SA_TYPE RestoreAction; SA_TYPE SaveAction; R_SIZE_T vsize; R_SIZE_T nsize; R_SIZE_T max_vsize; R_SIZE_T max_nsize; R_SIZE_T ppsize; int NoRenviron; #ifdef OSNAME_NT char *rhome; /* R_HOME */ char *home; /* HOME */ blah1 ReadConsole; blah2 WriteConsole; blah3 CallBack; blah4 ShowMessage; blah5 YesNoCancel; blah6 Busy; UImode CharacterMode; blah7 WriteConsoleEx; /* used only if WriteConsole is NULL */ #endif } structRstart; typedef structRstart *Rstart; void R_DefParams(Rstart); void R_SetParams(Rstart); #ifdef OSNAME_NT void R_SetWin32(Rstart); #endif void R_SizeFromEnv(Rstart); void R_common_command_line(int *, char **, Rstart); void R_set_command_line_arguments(int argc, char **argv); void setup_Rmainloop(void); extern FILE *R_Consolefile; extern FILE *R_Outputfile; extern void (*ptr_R_Suicide)(const char *); extern void (*ptr_R_ShowMessage)(const char *); extern int (*ptr_R_ReadConsole)(const char *, unsigned char *, int, int); extern void (*ptr_R_WriteConsole)(const char *, int); extern void (*ptr_R_WriteConsoleEx)(const char *, int, int); extern void (*ptr_R_ResetConsole)(void); extern void (*ptr_R_FlushConsole)(void); extern void (*ptr_R_ClearerrConsole)(void); extern void (*ptr_R_Busy)(int); extern void (*ptr_R_CleanUp)(SA_TYPE, int, int); extern int (*ptr_R_ShowFiles)(int, const char **, const char **, const char *, Rboolean, const char *); extern int (*ptr_R_ChooseFile)(int, char *, int); extern int (*ptr_R_EditFile)(const char *); extern void (*ptr_R_loadhistory)(SEXP, SEXP, SEXP, SEXP); extern void (*ptr_R_savehistory)(SEXP, SEXP, SEXP, SEXP); extern void (*ptr_R_addhistory)(SEXP, SEXP, SEXP, SEXP); // added in 3.0.0 extern int (*ptr_R_EditFiles)(int, const char **, const char **, const char *); // naming follows earlier versions in R.app extern SEXP (*ptr_do_selectlist)(SEXP, SEXP, SEXP, SEXP); extern SEXP (*ptr_do_dataentry)(SEXP, SEXP, SEXP, SEXP); extern SEXP (*ptr_do_dataviewer)(SEXP, SEXP, SEXP, SEXP); extern void (*ptr_R_ProcessEvents)(void); typedef unsigned int R_NativePrimitiveArgType; typedef struct { const char *name; DL_FUNC fun; int numArgs; R_NativePrimitiveArgType *types; } R_CMethodDef; typedef R_CMethodDef R_FortranMethodDef; typedef struct { const char *name; DL_FUNC fun; int numArgs; } R_CallMethodDef; typedef R_CallMethodDef R_ExternalMethodDef; typedef struct _DllInfo DllInfo; int R_registerRoutines(DllInfo *info, const R_CMethodDef * const croutines, const R_CallMethodDef * const callRoutines, const R_FortranMethodDef * const fortranRoutines, const R_ExternalMethodDef * const externalRoutines); DllInfo *R_getEmbeddingDllInfo(void); void *R_ExternalPtrAddr(SEXP s); rpy2-3.2.6/rpy2/rinterface_lib/embedded_mswin.py0000644000175000017500000000167613576515767023010 0ustar laurentlaurent00000000000000from . import embedded from . import callbacks from . import openrlib ffi = openrlib.ffi def _initr_win32(interactive: bool = True) -> int: embedded.rstart = ffi.new('Rstart') rstart = embedded.rstart rstart.rhome = openrlib.rlib.get_R_HOME() rstart.home = openrlib.rlib.getRUser() rstart.CharacterMode = openrlib.rlib.LinkDLL rstart.ReadConsole = callbacks._consoleread rstart.WriteConsole = callbacks._consolewrite_ex rstart.CallBack = callbacks._callback rstart.ShowMessage = callbacks._showmessage rstart.YesNoCancel = callbacks._yesnocancel rstart.Busy = callbacks._busy rstart.R_Quiet = True rstart.R_Interactive = interactive rstart.RestoreAction = openrlib.rlib.SA_RESTORE rstart.SaveAction = openrlib.rlib.SA_NOSAVE embedded.setinitialized() # TODO: still needed ? openrlib.rlib.R_CStackLimit = ffi.cast('uintptr_t', -1) openrlib.rlib.setup_Rmainloop() return 1 rpy2-3.2.6/rpy2/rinterface_lib/callbacks.py0000644000175000017500000002002013615570032021715 0ustar laurentlaurent00000000000000"""Callbacks available from R's C-API. The callbacks make available in R's C-API can be specified as Python functions, with the module providing the adapter code that makes it possible.""" from contextlib import contextmanager import logging import typing from .openrlib import ffi from .openrlib import _rinterface_cffi from . import ffi_proxy from . import conversion logger = logging.getLogger(__name__) # TODO: rename to "replace_in_module" @contextmanager def obj_in_module(module, name: str, obj): obj_orig = getattr(module, name) setattr(module, name, obj) try: yield finally: setattr(module, name, obj_orig) def consoleflush(): pass _FLUSHCONSOLE_EXCEPTION_LOG = 'R[flush console]: %s' @ffi_proxy.callback(ffi_proxy._consoleflush_def, _rinterface_cffi) def _consoleflush(): try: consoleflush() except Exception as e: logger.error(_FLUSHCONSOLE_EXCEPTION_LOG, str(e)) def consoleread(prompt: str) -> str: """Read input for the R console. :param prompt: The message prompted. :return: A string with the input returned by the user. """ return input(prompt) _READCONSOLE_EXCEPTION_LOG = 'R[read into console]: %s' _READCONSOLE_INTERNAL_EXCEPTION_LOG = ('Internal rpy2 error with ' '_consoleread callback: %s') @ffi_proxy.callback(ffi_proxy._consoleread_def, _rinterface_cffi) def _consoleread(prompt, buf, n: int, addtohistory) -> int: success = None try: s = conversion._cchar_to_str(prompt) reply = consoleread(s) except Exception as e: success = 0 logger.error(_READCONSOLE_EXCEPTION_LOG, str(e)) if success == 0: return success try: # TODO: Should the coding by dynamically extracted from # elsewhere ? reply_b = reply.encode('utf-8') reply_n = min(n, len(reply_b)) pybuf = bytearray(n) pybuf[:reply_n] = reply_b[:reply_n] ffi.memmove(buf, pybuf, n) if reply_n == 0: success = 0 else: success = 1 except Exception as e: success = 0 logger.error(_READCONSOLE_INTERNAL_EXCEPTION_LOG, str(e)) return success def consolereset() -> None: pass _RESETCONSOLE_EXCEPTION_LOG = 'R[reset console]: %s' @ffi_proxy.callback(ffi_proxy._consolereset_def, _rinterface_cffi) def _consolereset() -> None: try: consolereset() except Exception as e: logger.error(_RESETCONSOLE_EXCEPTION_LOG, str(e)) def consolewrite_print(s: str) -> None: """R writing to the console/terminal. :param s: the data to write to the console/terminal. """ # TODO: is the callback for flush working with Linux ? print(s, end='', flush=True) def consolewrite_warnerror(s: str) -> None: # TODO: use an rpy2/R-specific warning instead of UserWarning. logger.warning(_WRITECONSOLE_EXCEPTION_LOG, s) _WRITECONSOLE_EXCEPTION_LOG = 'R[write to console]: %s' @ffi_proxy.callback(ffi_proxy._consolewrite_ex_def, _rinterface_cffi) def _consolewrite_ex(buf, n: int, otype) -> None: s = conversion._cchar_to_str_with_maxlen(buf, maxlen=n) try: if otype == 0: consolewrite_print(s) else: consolewrite_warnerror(s) except Exception as e: logger.error(_WRITECONSOLE_EXCEPTION_LOG, str(e)) def showmessage(s: str) -> None: print('R wants to show a message') print(s) _SHOWMESSAGE_EXCEPTION_LOG = 'R[show message]: %s' @ffi_proxy.callback(ffi_proxy._showmessage_def, _rinterface_cffi) def _showmessage(buf): s = conversion._cchar_to_str(buf) try: showmessage(s) except Exception as e: logger.error(_SHOWMESSAGE_EXCEPTION_LOG, str(e)) def choosefile(new): return input('Enter file name:') _CHOOSEFILE_EXCEPTION_LOG = 'R[choose file]: %s' @ffi_proxy.callback(ffi_proxy._choosefile_def, _rinterface_cffi) def _choosefile(new, buf, n: int) -> int: try: res = choosefile(new) except Exception as e: logger.error(_CHOOSEFILE_EXCEPTION_LOG, str(e)) res = None if res is None: return 0 res_cdata = conversion._str_to_cchar(res) ffi.memmove(buf, res_cdata, len(res_cdata)) return len(res_cdata) def showfiles(filenames: typing.Tuple[str], headers: typing.Tuple[str], wtitle: str, pager: str) -> None: """R showing files. :param filenames: A tuple of file names. :param headers: A tuple of strings (TODO: check what it is) :wtitle: Title of the "window" showing the files. """ for fn in filenames: print('File: %s' % fn) with open(fn) as fh: for row in fh: print(row) print('---') _SHOWFILE_EXCEPTION_LOG = 'R[show file]: %s' _SHOWFILE_INTERNAL_EXCEPTION_LOG = ('Internal rpy2 error while ' 'showing files for R: %s') @ffi_proxy.callback(ffi_proxy._showfiles_def, _rinterface_cffi) def _showfiles(nfiles: int, files, headers, wtitle, delete, pager) -> int: filenames: typing.List[str] = [] headers_str = [] wtitle_str = None pager_str = None try: wtitle_str = conversion._cchar_to_str(wtitle) pager_str = conversion._cchar_to_str(pager) for i in range(nfiles): filenames.append(conversion._cchar_to_str(files[i])) headers_str.append(conversion._cchar_to_str(headers[i])) except Exception as e: logger.error(_SHOWFILE_INTERNAL_EXCEPTION_LOG, str(e)) if len(filenames): res = 0 else: res = 1 try: showfiles(tuple(filenames), tuple(headers_str), wtitle_str, pager_str) except Exception as e: res = 1 logger.error(_SHOWFILE_EXCEPTION_LOG, str(e)) return res def cleanup(saveact, status, runlast): pass _CLEANUP_EXCEPTION_LOG = 'R[cleanup]: %s' @ffi_proxy.callback(ffi_proxy._cleanup_def, _rinterface_cffi) def _cleanup(saveact, status, runlast): try: cleanup(saveact, status, runlast) except Exception as e: logger.error(_CLEANUP_EXCEPTION_LOG, str(e)) def processevents() -> None: """Process R events. This function can be periodically called by R to handle events such as window resizing in an interactive graphical device.""" pass _PROCESSEVENTS_EXCEPTION_LOG = 'R[processevents]: %s' @ffi_proxy.callback(ffi_proxy._processevents_def, _rinterface_cffi) def _processevents() -> None: try: processevents() except Exception as e: logger.error(_PROCESSEVENTS_EXCEPTION_LOG, str(e)) def busy(x: int) -> None: """R is busy. :param x: TODO this is an integer but do not know what it does. """ pass _BUSY_EXCEPTION_LOG = 'R[busy]: %s' @ffi_proxy.callback(ffi_proxy._busy_def, _rinterface_cffi) def _busy(which) -> None: try: busy(which) except Exception as e: logger.error(_BUSY_EXCEPTION_LOG, str(e)) def callback() -> None: pass _CALLBACK_EXCEPTION_LOG = 'R[callback]: %s' @ffi_proxy.callback(ffi_proxy._callback_def, _rinterface_cffi) def _callback() -> None: try: callback() except Exception as e: logger.error(_CALLBACK_EXCEPTION_LOG, str(e)) def yesnocancel(question: str) -> int: """Asking a user to answer yes, no, or cancel. :param question: The question asked to the user :return: An integer with the answer. """ return int(input(question)) _YESNOCANCEL_EXCEPTION_LOG = 'R[yesnocancel]: %s' @ffi_proxy.callback(ffi_proxy._yesnocancel_def, _rinterface_cffi) def _yesnocancel(question): try: q = conversion._cchar_to_str(question) res = yesnocancel(q) except Exception as e: logger.error(_YESNOCANCEL_EXCEPTION_LOG, str(e)) return res rpy2-3.2.6/rpy2/rinterface_lib/sexp.py0000644000175000017500000004545213576515767021021 0ustar laurentlaurent00000000000000"""Base definitions for R objects.""" import abc import collections.abc import enum import typing from . import embedded from . import memorymanagement from . import openrlib from . import _rinterface_capi as _rinterface from . import conversion class RTYPES(enum.IntEnum): """Native R types as defined in R's C API.""" NILSXP = openrlib.rlib.NILSXP SYMSXP = openrlib.rlib.SYMSXP LISTSXP = openrlib.rlib.LISTSXP CLOSXP = openrlib.rlib.CLOSXP ENVSXP = openrlib.rlib.ENVSXP PROMSXP = openrlib.rlib.PROMSXP LANGSXP = openrlib.rlib.LANGSXP SPECIALSXP = openrlib.rlib.SPECIALSXP BUILTINSXP = openrlib.rlib.BUILTINSXP CHARSXP = openrlib.rlib.CHARSXP LGLSXP = openrlib.rlib.LGLSXP INTSXP = openrlib.rlib.INTSXP REALSXP = openrlib.rlib.REALSXP CPLXSXP = openrlib.rlib.CPLXSXP STRSXP = openrlib.rlib.STRSXP DOTSXP = openrlib.rlib.DOTSXP ANYSXP = openrlib.rlib.ANYSXP VECSXP = openrlib.rlib.VECSXP EXPRSXP = openrlib.rlib.EXPRSXP BCODESXP = openrlib.rlib.BCODESXP EXTPTRSXP = openrlib.rlib.EXTPTRSXP WEAKREFSXP = openrlib.rlib.WEAKREFSXP RAWSXP = openrlib.rlib.RAWSXP S4SXP = openrlib.rlib.S4SXP NEWSXP = openrlib.rlib.NEWSXP FREESXP = openrlib.rlib.FREESXP FUNSXP = openrlib.rlib.FUNSXP class Sexp(object): """Base class for R objects. The name of a class corresponds to the name SEXP used in R's C API.""" __slots__ = ('_sexpobject', ) def __init__(self, sexp: typing.Union['Sexp', '_rinterface.SexpCapsule']): if not embedded.isready(): raise embedded.RNotReadyError('Embedded R is not ready to use.') if isinstance(sexp, Sexp): self._sexpobject = sexp.__sexp__ elif (isinstance(sexp, _rinterface.SexpCapsule) or isinstance(sexp, _rinterface.UnmanagedSexpCapsule)): self._sexpobject = sexp else: raise ValueError( 'The constructor must be called ' 'with an instance of rpy2.rinterface.Sexp ' 'or an instance of ' 'rpy2.rinterface._rinterface.SexpCapsule') def __repr__(self) -> str: return super().__repr__() + (' [%s]' % self.typeof) @property def __sexp__(self) -> '_rinterface.SexpCapsule': """Access to the underlying C pointer to the R object. When assigning a new SexpCapsule to this attribute, the R C-level type of the new capsule must be equal to the type of the old capsule. A ValueError is raised otherwise.""" return self._sexpobject @__sexp__.setter def __sexp__(self, value: '_rinterface.SexpCapsule') -> None: assert isinstance(value, _rinterface.SexpCapsule) if value.typeof != self.__sexp__.typeof: raise ValueError('New capsule type mismatch: %s' % RTYPES(value.typeof)) self._sexpobject = value @property def __sexp_refcount__(self) -> int: """Count the number of independent Python references to the underlying R object.""" return _rinterface._R_PRESERVED[ _rinterface.get_rid(self.__sexp__._cdata) ] def __getstate__(self) -> bytes: with memorymanagement.rmemory() as rmemory: ser = rmemory.protect( _rinterface.serialize(self.__sexp__._cdata, embedded.globalenv.__sexp__._cdata) ) n = openrlib.rlib.Rf_xlength(ser) res = bytes(_rinterface.ffi.buffer(openrlib.rlib.RAW(ser), n)) return res def __setstate__(self, state: bytes) -> None: self._sexpobject = unserialize(state) @property def rclass(self) -> 'StrSexpVector': """Get or set the R "class" attribute for the object.""" return rclass_get(self.__sexp__) @rclass.setter def rclass(self, value: 'typing.Union[StrSexpVector, str]'): rclass_set(self.__sexp__, value) @property def rid(self) -> int: """ID of the underlying R object (memory address).""" return _rinterface.get_rid(self.__sexp__._cdata) @property def typeof(self) -> RTYPES: return RTYPES(_rinterface._TYPEOF(self.__sexp__._cdata)) @property def named(self) -> int: return _rinterface._NAMED(self.__sexp__._cdata) @conversion._cdata_res_to_rinterface def list_attrs(self) -> 'typing.Union[StrSexpVector, str]': return _rinterface._list_attrs(self.__sexp__._cdata) @conversion._cdata_res_to_rinterface def do_slot(self, name: str) -> None: _rinterface._assert_valid_slotname(name) cchar = conversion._str_to_cchar(name) with memorymanagement.rmemory() as rmemory: name_r = rmemory.protect(openrlib.rlib.Rf_install(cchar)) if not _rinterface._has_slot(self.__sexp__._cdata, name_r): raise LookupError(name) res = openrlib.rlib.R_do_slot(self.__sexp__._cdata, name_r) return res def do_slot_assign(self, name: str, value) -> None: _rinterface._assert_valid_slotname(name) cchar = conversion._str_to_cchar(name) with memorymanagement.rmemory() as rmemory: name_r = rmemory.protect(openrlib.rlib.Rf_install(cchar)) cdata = rmemory.protect(conversion._get_cdata(value)) openrlib.rlib.R_do_slot_assign(self.__sexp__._cdata, name_r, cdata) @conversion._cdata_res_to_rinterface def get_attrib(self, name: str) -> 'Sexp': res = openrlib.rlib.Rf_getAttrib(self.__sexp__._cdata, conversion._str_to_charsxp(name)) return res # TODO: deprecate this (and implement __eq__) ? def rsame(self, sexp) -> bool: if isinstance(sexp, Sexp): return self.__sexp__._cdata == sexp.__sexp__._cdata elif isinstance(sexp, _rinterface.SexpCapsule): return sexp._cdata == sexp._cdata else: raise ValueError('Not an R object.') @property def names(self) -> 'Sexp': return embedded.baseenv['names'](self) @names.setter def names(self, value) -> None: if not isinstance(value, StrSexpVector): raise ValueError('The new names should be a StrSexpVector.') openrlib.rlib.Rf_namesgets( self.__sexp__._cdata, value.__sexp__._cdata) @property @conversion._cdata_res_to_rinterface def names_from_c_attribute(self) -> 'Sexp': return openrlib.rlib.Rf_getAttrib( self.__sexp__._cdata, openrlib.rlib.R_NameSymbol) # TODO: Duplicate declaration in R_API.h ? class CETYPE(enum.Enum): """Character encodings for R string.""" CE_NATIVE = 0 CE_UTF8 = 1 CE_LATIN1 = 2 CE_BYTES = 3 CE_SYMBOL = 5 CE_ANY = 99 class NCHAR_TYPE(enum.Enum): """Type of string scalar in R.""" Bytes = 0 Chars = 1 Width = 2 class CharSexp(Sexp): """R's internal (C API-level) scalar for strings.""" _R_TYPE = openrlib.rlib.CHARSXP _NCHAR_MSG = openrlib.ffi.new('char []', b'rpy2.rinterface.CharSexp.nchar') @property def encoding(self) -> CETYPE: return CETYPE( openrlib.rlib.Rf_getCharCE(self.__sexp__._cdata) ) def nchar(self, what: NCHAR_TYPE = NCHAR_TYPE.Bytes) -> int: # TODO: nchar_type is not parsed properly by cffi ? return openrlib.rlib.R_nchar(self.__sexp__._cdata, what.value, openrlib.rlib.FALSE, openrlib.rlib.FALSE, self._NCHAR_MSG) # TODO: move to _rinterface-level function (as ABI / API compatibility # will have API-defined code compile for efficiency). def _populate_r_vector(iterable, r_vector, set_elt, cast_value): for i, v in enumerate(iterable): set_elt(r_vector, i, cast_value(v)) VT = typing.TypeVar('VT', bound='SexpVector') class SexpVector(Sexp, metaclass=abc.ABCMeta): """Base abstract class for R vector objects. R vector objects are, at the C level, essentially C arrays wrapped in the general structure for R objects.""" @property @abc.abstractmethod def _R_TYPE(self): pass @property @abc.abstractmethod def _R_SIZEOF_ELT(self): pass @staticmethod @abc.abstractmethod def _CAST_IN(o): pass @staticmethod @abc.abstractmethod def _R_SET_VECTOR_ELT(x, i, v): pass @staticmethod @abc.abstractmethod def _R_VECTOR_ELT(x, i): pass @staticmethod @abc.abstractmethod def _R_GET_PTR(o): pass def __init__(self, obj: typing.Union[_rinterface.SexpCapsule, collections.abc.Sized]): if isinstance(obj, Sexp) or isinstance(obj, _rinterface.SexpCapsule): super().__init__(obj) elif isinstance(obj, collections.abc.Sized): super().__init__(type(self).from_object(obj).__sexp__) else: raise TypeError('The constructor must be called ' 'with an instance of ' 'rpy2.rinterface.Sexp ' 'or an instance of ' 'rpy2.rinterface._rinterface.SexpCapsule') @classmethod def _populate_r_vector(cls, iterable, r_vector): return _populate_r_vector(iterable, r_vector, cls._R_SET_VECTOR_ELT, cls._CAST_IN) @classmethod @conversion._cdata_res_to_rinterface def from_iterable(cls, iterable, populate_func=None) -> VT: """Create an R vector/array from an iterable.""" if not embedded.isready(): raise embedded.RNotReadyError('Embedded R is not ready to use.') n = len(iterable) with memorymanagement.rmemory() as rmemory: r_vector = rmemory.protect( openrlib.rlib.Rf_allocVector( cls._R_TYPE, n) ) if populate_func is None: cls._populate_r_vector(iterable, r_vector) else: populate_func(iterable, r_vector) return r_vector @classmethod @conversion._cdata_res_to_rinterface def from_memoryview(cls, mview: memoryview) -> VT: """Create an R vector/array from a memoryview. The memoryview must be contiguous, and the C representation for the vector must be compatible between R and Python. If not the case, a :class:`ValueError` exception with will be raised.""" if not embedded.isready(): raise embedded.RNotReadyError('Embedded R is not ready to use.') if not mview.contiguous: raise ValueError('The memory view must be contiguous.') if mview.itemsize != cls._R_SIZEOF_ELT: msg = ( 'Incompatible C type sizes. ' 'The R array type is {r_size} bytes ' 'while the Python array type is {py_size} ' 'bytes.' .format(r_size=cls._R_SIZEOF_ELT, py_size=mview.itemsize) ) raise ValueError(msg) r_vector = None n = len(mview) with memorymanagement.rmemory() as rmemory: r_vector = rmemory.protect( openrlib.rlib.Rf_allocVector( cls._R_TYPE, n) ) dest_ptr = cls._R_GET_PTR(r_vector) src_ptr = _rinterface.ffi.from_buffer(mview) nbytes = n * mview.itemsize _rinterface.ffi.memmove(dest_ptr, src_ptr, nbytes) return r_vector @classmethod def from_object(cls, obj) -> VT: """Create an R vector/array from a Python object, if possible. An exception :class:`ValueError` will be raised if not possible.""" res = None try: mv = memoryview(obj) res = cls.from_memoryview(mv) except (TypeError, ValueError): try: res = cls.from_iterable(obj) except ValueError: msg = ('The class methods from_memoryview() and ' 'from_iterable() both failed to make a {} ' 'from an object of class {}' .format(cls, type(obj))) raise ValueError(msg) return res def __getitem__( self, i: typing.Union[int, slice]) -> typing.Union[Sexp, VT]: 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 = type(self).from_iterable( [ self._R_VECTOR_ELT( cdata, i_c ) for i_c in range(*i.indices(len(self))) ], populate_func=lambda iterable, r_vector: _populate_r_vector( iterable, r_vector, self._R_SET_VECTOR_ELT, lambda x: x) ) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) return res def __setitem__(self, i: typing.Union[int, slice], value) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) self._R_SET_VECTOR_ELT(cdata, i_c, value.__sexp__._cdata) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): self._R_SET_VECTOR_ELT(cdata, i_c, v.__sexp__._cdata) else: raise TypeError( 'Indices must be integers or slices, not %s' % type(i)) def __len__(self) -> int: return openrlib.rlib.Rf_xlength(self.__sexp__._cdata) def index(self, item) -> int: for i, e in enumerate(self): if e == item: return i raise ValueError("'%s' is not in R vector" % item) def _as_charsxp_cdata(x: typing.Union[CharSexp, str]): if isinstance(x, CharSexp): return x.__sexp__._cdata else: return conversion._str_to_charsxp(x) class StrSexpVector(SexpVector): """R vector of strings.""" _R_TYPE = openrlib.rlib.STRSXP _R_GET_PTR = openrlib._STRING_PTR _R_SIZEOF_ELT = None _R_VECTOR_ELT = openrlib.rlib.STRING_ELT _R_SET_VECTOR_ELT = openrlib.rlib.SET_STRING_ELT _CAST_IN = _as_charsxp_cdata def __getitem__( self, i: typing.Union[int, slice]) -> 'typing.Union[str, StrSexpVector]': cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) res = _rinterface._string_getitem(cdata, i_c) elif isinstance(i, slice): res = type(self).from_iterable( [_rinterface._string_getitem(cdata, i_c) for i_c in range(*i.indices(len(self)))] ) else: raise TypeError('Indices must be integers or slices,' ' not %s' % type(i)) return res def __setitem__(self, i: typing.Union[int, slice], value: typing.Union[str, typing.Sequence[str]]) -> None: cdata = self.__sexp__._cdata if isinstance(i, int): i_c = _rinterface._python_index_to_c(cdata, i) self._R_SET_VECTOR_ELT( cdata, i_c, _as_charsxp_cdata(value) ) elif isinstance(i, slice): for i_c, v in zip(range(*i.indices(len(self))), value): self._R_SET_VECTOR_ELT( cdata, i_c, _as_charsxp_cdata(v) ) else: raise TypeError('Indices must be integers or slices, ' 'not %s' % type(i)) def get_charsxp(self, i: int) -> CharSexp: """Get the R CharSexp objects for the index i.""" i_c = _rinterface._python_index_to_c(self.__sexp__._cdata, i) return CharSexp( _rinterface.SexpCapsule( openrlib.rlib.STRING_ELT(self.__sexp__._cdata, i_c) ) ) # TODO: complete class names. _DEFAULT_RCLASS_NAMES = { RTYPES.ENVSXP: 'environment', RTYPES.CLOSXP: 'function', RTYPES.SPECIALSXP: 'function', RTYPES.BUILTINSXP: 'function', RTYPES.REALSXP: 'numeric', RTYPES.STRSXP: 'character', RTYPES.SYMSXP: 'name', RTYPES.VECSXP: 'list', RTYPES.LANGSXP: 'language'} def rclass_get(scaps: _rinterface.SexpCapsule) -> 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_set( scaps: _rinterface.SexpCapsule, value: 'typing.Union[StrSexpVector, str]' ) -> None: if isinstance(value, StrSexpVector): value_r = value elif isinstance(value, str): value_r = StrSexpVector.from_iterable( [value]) else: raise TypeError('Value should a str or ' 'a rpy2.rinterface.sexp.StrSexpVector.') openrlib.rlib.Rf_setAttrib(scaps._cdata, openrlib.rlib.R_ClassSymbol, value_r.__sexp__._cdata) def unserialize(state): n = len(state) with memorymanagement.rmemory() as rmemory: cdata = rmemory.protect( openrlib.rlib.Rf_allocVector(openrlib.rlib.RAWSXP, n)) _rinterface.ffi.memmove( openrlib.rlib.RAW(cdata), state, n) ser = rmemory.protect( _rinterface.unserialize(cdata, embedded.globalenv.__sexp__._cdata) ) res = _rinterface.SexpCapsule(ser) return res rpy2-3.2.6/rpy2/rinterface_lib/R_API_eventloop.h0000644000175000017500000000314513576515767022617 0ustar laurentlaurent00000000000000/* cffi_source-begin */ # include /* for fd_set */ /* cffi_source-end */ typedef void (*InputHandlerProc)(void *userData); typedef struct _InputHandler InputHandler; typedef struct _InputHandler { int activity; int fileDescriptor; InputHandlerProc handler; struct _InputHandler *next; /* Whether we should be listening to this file descriptor or not. */ int active; /* Data that can be passed to the routine as its only argument. This might be a user-level function or closure when we implement a callback to R mechanism. */ void *userData; } InputHandler; extern InputHandler *initStdinHandler(void); extern InputHandler *addInputHandler(InputHandler *handlers, int fd, InputHandlerProc handler, int activity); extern InputHandler *getInputHandler(InputHandler *handlers, int fd); extern int removeInputHandler(InputHandler **handlers, InputHandler *it); extern InputHandler *R_InputHandlers; extern void (* R_PolledEvents)(void); extern int R_wait_usec; /* cffi_source-begin */ /* The definitions below require fd_set, which is only defined through * the include of sys/select.h . */ extern InputHandler *getSelectedHandler(InputHandler *handlers, fd_set *mask); extern fd_set *R_checkActivity(int usec, int ignore_stdin); extern fd_set *R_checkActivityEx(int usec, int ignore_stdin, void (*intr)(void)); extern void R_runHandlers(InputHandler *handlers, fd_set *mask); extern int R_SelectEx(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout, void (*intr)(void)); /* cffi_source-end */ rpy2-3.2.6/doc/0000755000175000017500000000000013615572523014342 5ustar laurentlaurent00000000000000rpy2-3.2.6/doc/Makefile0000644000175000017500000002012613576515767016020 0ustar laurentlaurent00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \'make \' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " rpy2demo_graphics to build the figures used in the documentation about graphics" @echo " rpy2demo_benchmark to build the figure(s) used in the documentation about benchmarks" @echo " notebooks to build the ipython notebooks used in the documentation" clean: rm -rf $(BUILDDIR)/* rpy2demo_benchmark: @cd _static/demos && python benchmarks.py rpy2demo_graphics: @cd _static/demos && python graphics.py rpy2demo_all: rpy2demo_graphics rpy2demo_benchmark # rule to build ipython notebooks from markdown notebooks/%.ipynb : notebooks/%.md notedown \ --precode 'from functools import partial' \ 'from rpy2.ipython import html' \ 'html.html_rdataframe=partial(html.html_rdataframe, table_class="docutils")' \ --run \ -o $@ \ $< cp -p $@ _static/notebooks/ # rule to build sphinx-friendly ReST from ipython notebooks notebooks/%.rst : notebooks/%.ipynb @cd generated_rst && jupyter nbconvert --to rst ../$< --output-dir=. # rule to build HTML render of an ipython notebook _static/notebooks/%.html : notebooks/%.ipynb jupyter nbconvert --to html $< --output ../$@ MD_NOTEBOOKS := $(basename $(notdir $(wildcard notebooks/*.md))) _ensure_notebooks_dir: mkdir -p _static/notebooks _notebooks: $(MD_NOTEBOOKS:%=notebooks/%.rst) _notebooks_html: $(MD_NOTEBOOKS:%=_static/notebooks/%.html) _notebooks_rst: $(MD_NOTEBOOKS:%=notebooks/%.rst) notebooks: _ensure_notebooks_dir _notebooks _notebooks_html _notebooks_rst html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/rpy2.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/rpy2.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/rpy2" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/rpy2" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." rpy2-3.2.6/setup.py0000755000175000017500000001531113615570032015304 0ustar laurentlaurent00000000000000#!/usr/bin/env python import sys if ((sys.version_info[0] < 3) or (sys.version_info[0] == 3 and sys.version_info[1] < 5)): print('rpy2 is no longer supporting Python < 3.5.' 'Consider using an older rpy2 release when using an ' 'older Python release.') sys.exit(1) import enum import os import shutil import subprocess import tempfile import warnings from distutils.ccompiler import new_compiler from distutils.sysconfig import customize_compiler from distutils.errors import CCompilerError, DistutilsExecError, DistutilsPlatformError from rpy2 import situation from setuptools import setup PACKAGE_NAME = 'rpy2' pack_version = __import__('rpy2').__version__ package_prefix='.' R_MIN_VERSION = (3, 3) def _format_version(x): return '.'.join(map(str, x)) def cmp_version(x, y): if (x[0] < y[0]): return -1 if (x[0] > y[0]): return 1 if (x[0] == y[0]): if len(x) == 1 or len(y) == 1: return 0 return cmp_version(x[1:], y[1:]) class COMPILATION_STATUS(enum.Enum): COMPILE_ERROR=('unable to compile R C extensions - missing headers ' 'or R not compiled as a library ?') NO_COMPILER=('unable to compile sqlite3 C extensions - ' 'no c compiler?') PLATFORM_ERROR=('unable to compile R C extensions - platform error') OK = None def get_c_extension_status(libraries=['R'], include_dirs=None, library_dirs=None): c_code = ('#include \n\n' 'int main(int argc, char **argv) { return 0; }') tmp_dir = tempfile.mkdtemp(prefix='tmp_pw_r_') bin_file = os.path.join(tmp_dir, 'test_pw_r') src_file = bin_file + '.c' with open(src_file, 'w') as fh: fh.write(c_code) compiler = new_compiler() customize_compiler(compiler) try: compiler.link_executable( compiler.compile([src_file], output_dir=tmp_dir, include_dirs=include_dirs), bin_file, libraries=libraries, library_dirs=library_dirs) except CCompilerError: status = COMPILATION_STATUS.COMPILE_ERROR except DistutilsExecError: status = COMPILATION_STATUS.NO_COMPILER except DistutilsPlatformError: status = COMPILATION_STATUS.PLATFORM_ERROR else: status = COMPILATION_STATUS.OK shutil.rmtree(tmp_dir) return status def get_r_c_extension_status(): r_home = situation.get_r_home() if r_home is None: print('There is no R_HOME and the R executable cannot be found.') sys.exit(1) c_ext = situation.CExtensionOptions() c_ext.add_lib( *situation.get_r_flags(r_home, '--ldflags') ) c_ext.add_include( *situation.get_r_flags(r_home, '--cppflags') ) status = get_c_extension_status(libraries=c_ext.libraries, include_dirs=c_ext.include_dirs, library_dirs=c_ext.library_dirs) return status c_extension_status = get_r_c_extension_status() cffi_mode = situation.get_cffi_mode() if cffi_mode == situation.CFFI_MODE.ABI: cffi_modules = ['rpy2/_rinterface_cffi_build.py:ffibuilder_abi'] elif cffi_mode == situation.CFFI_MODE.API: if c_extension_status != COMPILATION_STATUS.OK: print('API mode requested but %s' % c_extension_status.value) sys.exit(1) cffi_modules = ['rpy2/_rinterface_cffi_build.py:ffibuilder_api'] elif cffi_mode == situation.CFFI_MODE.BOTH: if c_extension_status != COMPILATION_STATUS.OK: print('API mode requested but %s' % c_extension_status.value) sys.exit(1) cffi_modules = ['rpy2/_rinterface_cffi_build.py:ffibuilder_abi', 'rpy2/_rinterface_cffi_build.py:ffibuilder_api'] else: # default interface cffi_modules = ['rpy2/_rinterface_cffi_build.py:ffibuilder_abi'] if c_extension_status == COMPILATION_STATUS.OK: cffi_modules.append('rpy2/_rinterface_cffi_build.py:ffibuilder_api') LONG_DESCRIPTION = """ Python interface to the R language. `rpy2` is running an embedded R, providing access to it from Python using R's own C-API through either: - a high-level interface making R functions an objects just like Python functions and providing a seamless conversion to numpy and pandas data structures - a low-level interface closer to the C-API It is also providing features for when working with jupyter notebooks or ipython. """ if __name__ == '__main__': pack_dir = {PACKAGE_NAME: os.path.join(package_prefix, 'rpy2')} requires = ['pytest', 'jinja2', 'pytz', 'simplegeneric', 'tzlocal'] setup( name=PACKAGE_NAME, version=pack_version, description='Python interface to the R language (embedded R)', long_description=LONG_DESCRIPTION, url='https://rpy2.bitbucket.io', license='GPLv2+', author='Laurent Gautier', author_email='lgautier@gmail.com', requires=requires, install_requires=requires + ['cffi>=1.13.1'], setup_requires=['cffi>=1.13.1'], cffi_modules=cffi_modules, package_dir=pack_dir, packages=([PACKAGE_NAME] + ['{pack_name}.{x}'.format(pack_name=PACKAGE_NAME, x=x) for x in ('rlike', 'rinterface_lib', 'robjects', 'robjects.lib', 'interactive', 'ipython', 'tests', 'tests.rinterface', 'tests.rlike', 'tests.robjects', 'tests.ipython', 'tests.robjects.lib')] ), classifiers = ['Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', ('License :: OSI Approved :: GNU General ' 'Public License v2 or later (GPLv2+)'), 'Intended Audience :: Developers', 'Intended Audience :: Science/Research', 'Development Status :: 5 - Production/Stable' ], package_data={'rpy2': ['rinterface_lib/R_API.h', 'rinterface_lib/R_API_eventloop.h']} ) print('---') if 'rpy2/_rinterface_cffi_build.py:ffibuilder_abi' in cffi_modules: print('ABI mode interface built and installed') if 'rpy2/_rinterface_cffi_build.py:ffibuilder_api' in cffi_modules: print('API mode interface built and installed') else: print('API mode interface not build because: %s' % c_extension_status) rpy2-3.2.6/rpy2.egg-info/0000755000175000017500000000000013615572523016163 5ustar laurentlaurent00000000000000rpy2-3.2.6/rpy2.egg-info/SOURCES.txt0000644000175000017500000001015713615572523020053 0ustar laurentlaurent00000000000000AUTHORS MANIFEST.in NEWS README.rst gpl-2.0.txt setup.py ./rpy2/__init__.py ./rpy2/_rinterface_cffi_build.py ./rpy2/rinterface.py ./rpy2/situation.py ./rpy2/interactive/__init__.py ./rpy2/interactive/packages.py ./rpy2/interactive/process_revents.py ./rpy2/ipython/__init__.py ./rpy2/ipython/ggplot.py ./rpy2/ipython/html.py ./rpy2/ipython/rmagic.py ./rpy2/rinterface_lib/R_API.h ./rpy2/rinterface_lib/R_API_eventloop.h ./rpy2/rinterface_lib/__init__.py ./rpy2/rinterface_lib/_rinterface_capi.py ./rpy2/rinterface_lib/bufferprotocol.py ./rpy2/rinterface_lib/callbacks.py ./rpy2/rinterface_lib/conversion.py ./rpy2/rinterface_lib/embedded.py ./rpy2/rinterface_lib/embedded_mswin.py ./rpy2/rinterface_lib/ffi_proxy.py ./rpy2/rinterface_lib/memorymanagement.py ./rpy2/rinterface_lib/na_values.py ./rpy2/rinterface_lib/openrlib.py ./rpy2/rinterface_lib/sexp.py ./rpy2/rlike/__init__.py ./rpy2/rlike/container.py ./rpy2/rlike/functional.py ./rpy2/rlike/indexing.py ./rpy2/robjects/__init__.py ./rpy2/robjects/constants.py ./rpy2/robjects/conversion.py ./rpy2/robjects/environments.py ./rpy2/robjects/functions.py ./rpy2/robjects/help.py ./rpy2/robjects/language.py ./rpy2/robjects/methods.py ./rpy2/robjects/numpy2ri.py ./rpy2/robjects/packages.py ./rpy2/robjects/packages_utils.py ./rpy2/robjects/pandas2ri.py ./rpy2/robjects/robject.py ./rpy2/robjects/vectors.py ./rpy2/robjects/lib/__init__.py ./rpy2/robjects/lib/dbplyr.py ./rpy2/robjects/lib/dplyr.py ./rpy2/robjects/lib/ggplot2.py ./rpy2/robjects/lib/grdevices.py ./rpy2/robjects/lib/grid.py ./rpy2/robjects/lib/tidyr.py ./rpy2/tests/__init__.py ./rpy2/tests/utils.py ./rpy2/tests/ipython/__init__.py ./rpy2/tests/ipython/test_ggplot.py ./rpy2/tests/ipython/test_html.py ./rpy2/tests/ipython/test_rmagic.py ./rpy2/tests/rinterface/__init__.py ./rpy2/tests/rinterface/test_bufferprotocol.py ./rpy2/tests/rinterface/test_callbacks.py ./rpy2/tests/rinterface/test_embedded_r.py ./rpy2/tests/rinterface/test_endr.py ./rpy2/tests/rinterface/test_environment.py ./rpy2/tests/rinterface/test_externalptr.py ./rpy2/tests/rinterface/test_functions.py ./rpy2/tests/rinterface/test_memorymanagement.py ./rpy2/tests/rinterface/test_na.py ./rpy2/tests/rinterface/test_noinitialization.py ./rpy2/tests/rinterface/test_openrlib.py ./rpy2/tests/rinterface/test_sexp.py ./rpy2/tests/rinterface/test_symbol.py ./rpy2/tests/rinterface/test_vector_bool.py ./rpy2/tests/rinterface/test_vector_byte.py ./rpy2/tests/rinterface/test_vector_complex.py ./rpy2/tests/rinterface/test_vector_float.py ./rpy2/tests/rinterface/test_vector_int.py ./rpy2/tests/rinterface/test_vector_lang.py ./rpy2/tests/rinterface/test_vector_list.py ./rpy2/tests/rinterface/test_vector_numpy.py ./rpy2/tests/rinterface/test_vector_pairlist.py ./rpy2/tests/rinterface/test_vector_str.py ./rpy2/tests/rinterface/test_vectors.py ./rpy2/tests/rlike/test_container.py ./rpy2/tests/rlike/test_functional.py ./rpy2/tests/rlike/test_indexing.py ./rpy2/tests/robjects/__init__.py ./rpy2/tests/robjects/test_array.py ./rpy2/tests/robjects/test_conversion.py ./rpy2/tests/robjects/test_conversion_numpy.py ./rpy2/tests/robjects/test_dataframe.py ./rpy2/tests/robjects/test_environment.py ./rpy2/tests/robjects/test_formula.py ./rpy2/tests/robjects/test_function.py ./rpy2/tests/robjects/test_help.py ./rpy2/tests/robjects/test_language.py ./rpy2/tests/robjects/test_methods.py ./rpy2/tests/robjects/test_packages.py ./rpy2/tests/robjects/test_packages_utils.py ./rpy2/tests/robjects/test_pandas_conversions.py ./rpy2/tests/robjects/test_robjects.py ./rpy2/tests/robjects/test_rs4.py ./rpy2/tests/robjects/test_serialization.py ./rpy2/tests/robjects/test_translated_function.py ./rpy2/tests/robjects/test_vector.py ./rpy2/tests/robjects/test_vector_datetime.py ./rpy2/tests/robjects/test_vector_extractdelegator.py ./rpy2/tests/robjects/test_vector_factor.py ./rpy2/tests/robjects/lib/test_dplyr.py ./rpy2/tests/robjects/lib/test_ggplot2.py ./rpy2/tests/robjects/lib/test_grdevices.py ./rpy2/tests/robjects/lib/test_grid.py ./rpy2/tests/robjects/lib/test_tidyr.py doc/Makefile rpy2.egg-info/PKG-INFO rpy2.egg-info/SOURCES.txt rpy2.egg-info/dependency_links.txt rpy2.egg-info/requires.txt rpy2.egg-info/top_level.txtrpy2-3.2.6/rpy2.egg-info/requires.txt0000644000175000017500000000006613615572523020565 0ustar laurentlaurent00000000000000cffi>=1.13.1 jinja2 pytest pytz simplegeneric tzlocal rpy2-3.2.6/rpy2.egg-info/top_level.txt0000644000175000017500000000005713615572523020717 0ustar laurentlaurent00000000000000_rinterface_cffi_abi _rinterface_cffi_api rpy2 rpy2-3.2.6/rpy2.egg-info/PKG-INFO0000644000175000017500000000250613615572523017263 0ustar laurentlaurent00000000000000Metadata-Version: 1.1 Name: rpy2 Version: 3.2.6 Summary: Python interface to the R language (embedded R) Home-page: https://rpy2.bitbucket.io Author: Laurent Gautier Author-email: lgautier@gmail.com License: GPLv2+ Description: Python interface to the R language. `rpy2` is running an embedded R, providing access to it from Python using R's own C-API through either: - a high-level interface making R functions an objects just like Python functions and providing a seamless conversion to numpy and pandas data structures - a low-level interface closer to the C-API It is also providing features for when working with jupyter notebooks or ipython. Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: Development Status :: 5 - Production/Stable Requires: pytest Requires: jinja2 Requires: pytz Requires: simplegeneric Requires: tzlocal rpy2-3.2.6/rpy2.egg-info/dependency_links.txt0000644000175000017500000000000113615572523022231 0ustar laurentlaurent00000000000000 rpy2-3.2.6/AUTHORS0000644000175000017500000000112013576515767014654 0ustar laurentlaurent00000000000000 Author ------ Laurent Gautier Copyright Laurent Gautier 2008-2010 People have contributed suggestions or patches; they are thanked here, rpy2 is much better because of them. rpy2 is making a limited use (if much left) of code from: RPy - http://rpy.sourceforge.net -------------------------------- (in rinteface/rinterface.c) Copyright Walter Moreira 2002-2003 Copyright Gregory Warnes 2003-2008 Parseltongue project - http://serpent.speak.googlepages.com/ ------------------------------------------------------------ (in rinterface/rinterface.c) Copyright Alexander Belopolsky - 2006 rpy2-3.2.6/MANIFEST.in0000644000175000017500000000065413576515767015355 0ustar laurentlaurent00000000000000global-include README global-exclude *patch* *diff* .hg include MANIFEST MANIFEST.in include NEWS README AUTHORS include gpl-2.0.txt include _rinterface_cffi_build include rpy/__init__.py include rpy/situation.py recursive-include rpy/robjects *.py recursive-include rpy/ipython *.py recursive-include rpy/interactive *.py recursive-include rpy/rlike *.py prune dist include doc/Makefile include doc/source/rpy2_logo.png rpy2-3.2.6/gpl-2.0.txt0000644000175000017500000004325413576515767015442 0ustar laurentlaurent00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. rpy2-3.2.6/README.rst0000644000175000017500000000460013615570032015255 0ustar laurentlaurent00000000000000.. image:: doc/_static/rpy2_logo_64x64.png .. image:: https://img.shields.io/pypi/v/rpy2.svg?style=flat-square :target: https://pypi.python.org/pypi/rpy2 .. image:: https://travis-ci.com/rpy2/rpy2.svg?branch=master :target: https://travis-ci.com/rpy2/rpy2 .. image:: https://codecov.io/gh/rpy2/rpy2/branch/master/graph/badge.svg :target: https://codecov.io/gh/rpy2/rpy2 Installation ============ `pip` should work out of the box: pip install rpy2 The package is known to compile on Linux, MacOSX, and Windows (provided that developper tools are installed, and you are ready figure out how by yourself). Alternatively, there is a Docker image available to try rpy2 out without concerns about the installation process. To run the ipython console: docker run -it --rm -p 8888:8888 rpy2/jupyter:latest ipython To run jupypter notebook on port 8888: docker run --rm -p 8888:8888 rpy2/jupyter:latest More information about Docker images can be found in the `documentation `_. In case you find yourself with this source without any idea of what it takes to compile anything on your platform, try first python setup.py install If this fails, consider looking for pre-compiled binaries (they are available on Linux Red Hat, CentOS, Debian, Ubuntu, etc...) or using the matching Docker container. Note that `python setup.py develop` will appear to work, but will result in an installation from the `rpy` directory here. The namespaces will be incorrect, so don't do that! Documentation ============= Documentation is available either in the source tree (to be built), or online (on readthedocs). Testing ======= `rpy2` is now relying on `pytest`, with the plugin `pytest-cov` for code coverage. To test the package from the source tree, either to check and installation on your system or before submitting a pull request, do: pytest tests/ For code coverage, do: pytest --cov=rpy2.rinterface_lib \ --cov=rpy2.rinterface \ --cov=rpy2.ipython \ --cov=rpy2.robject \ tests For more options, such as how to run specify tests, please refer to the `pytest` documentation. License ======= RPy2 can be used under the terms of the GNU General Public License Version 2 or later (see the file gpl-2.0.txt). This is the very same license R itself is released under. rpy2-3.2.6/setup.cfg0000644000175000017500000000004613615572523015416 0ustar laurentlaurent00000000000000[egg_info] tag_build = tag_date = 0 rpy2-3.2.6/PKG-INFO0000644000175000017500000000250613615572523014675 0ustar laurentlaurent00000000000000Metadata-Version: 1.1 Name: rpy2 Version: 3.2.6 Summary: Python interface to the R language (embedded R) Home-page: https://rpy2.bitbucket.io Author: Laurent Gautier Author-email: lgautier@gmail.com License: GPLv2+ Description: Python interface to the R language. `rpy2` is running an embedded R, providing access to it from Python using R's own C-API through either: - a high-level interface making R functions an objects just like Python functions and providing a seamless conversion to numpy and pandas data structures - a low-level interface closer to the C-API It is also providing features for when working with jupyter notebooks or ipython. Platform: UNKNOWN Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: Development Status :: 5 - Production/Stable Requires: pytest Requires: jinja2 Requires: pytz Requires: simplegeneric Requires: tzlocal rpy2-3.2.6/NEWS0000644000175000017500000020767613615572421014313 0ustar laurentlaurent00000000000000Release 3.2.6 ============= Bugs fixed ---------- - The conversion of date/time object with specified timezones was wrong when different than the local time zone (issue #634) - Iterating over :mod:`rpy2.situation.iter_info()` could result in a error because of a typo in the code. Changes ------- - :mod:`pandas` 1.0.0 breaks the conversion layer. A warning is not emitted whenever trying to use `pandas` >= 1.0. Release 3.2.5 ============= Bugs fixed ---------- - Latest release for R package `rlang` broke import through `importr()`. A workaround for :mod:`rpy2.robjects.lib.ggplot2` is to rename the offending R object (issue #631). Changes ------- - f-string requiring Python >= 3.6 removed. Release 3.2.4 ============= Bugs fixed ---------- - An incomplete backport of the bug fixed in 3.2.3 broke the ABI mode. Release 3.2.3 ============= Bugs fixed ---------- - Error when parsing strings as R codes could result in a segfault. Release 3.2.2 ============= Bugs fixed ---------- - Python format error when trying to report that the system is not reported on Windows (issue #597). - The setup script would error on build if R is not installed. It is now printing an error message. Release 3.2.1 ============= Bugs fixed ---------- - The wrapper for the R package `dbplyr` could not import the underlying package (refactoring elsewhere was not propagated there). - Creating R objects called `names` `globalenv` caused the method :meth:`Sexp.names` to fail (issue #587). - Whenever the pandas conversion was activated :class:`FloatSexpVector` instances with the R class `POSIXct` attached where not corrected mapped back to pandas datetime arrays. (issue #594). - Fix installation when an installation when a prefix without write access is used (issue #588). Release 3.2.0 ============= New features ------------ - rpy2 can built and used with :mod:`cffi`'s ABI or API modes (releases 3.0.x and 3.1.x were using the ABI mode exclusively). At the time of writing the default is still the ABI mode but the choice can be controlled through the environment variable `RPY2_CFFI_MODE`. If set, possible values are `ABI` (default if the environment variable is not set), `API`, or `BOTH`. When the latter, both `API` and `ABI` modes are built, and the choice of which one to use can be made at run time. Changes ------- - The "consoleread" callback (reading input to the R console) is now assuming UTF-8 (was previously assuming ASCII) and is no longer trying to add a "new line" character at the end of the input. - Querying an R environment with an invalid key will generate a :class:`TypeError` or a :class:`ValueError` depending on the issue (rather than always :class:`ValueError` before. Bugs fixed ---------- - `setup.py` is now again compatible with Python2 (issue #580). - Unit tests were failing if numpy is not installed. - :mod:`rpy2.situation` is no longer breaking when R is not the in path and there is no environment variable `R_HOME`. - Build script for the cffi interface is now using the environment variable `R_HOME` whenever defined (rather that always infer it from the R in the PATH). - Converting R strings back to Python was incorrectly using `Latin1` while `UTF-8` was intended (issue #537). Release 3.1.0 ============ New features ------------ - Python matrix multiplication (`__matmul__` / `@`) added to R :class:`Matrix` objects. - An :class:`threading.RLock` is added to :mod:`rpy2.rinterface_lib.openrlib` and is used by the context manager :func:`rpy2.rinterface_lib.memorymanagement.rmemory` to ensure that protect/unprotect cycles cannot be broken by thread switching, at least as long as the context manager is used to handle such cycles (see issue #571). - The documentation covers the use of notebooks (mainly Jupyter/Jupyterlab). - The PNG output in Jupyter notebooks R cells can now specify an argument `--type` (passed as the named argument `type` in the R function `png`). For example on some Linux systems and R installations, the type `cairo` can fix issues when alpha transparency is used. Changes ------- - Added callbacks for `ptr_R_Busy()` and `ptr_R_ProcessEvents()`. - `rstart` now an objects in :mod:`rpy2.rinterface_lib.embedded` (set to `None` until R is initialized). - Unit tests are included in a subpackage :mod:`rpy2.tests` as was the case before release 3.0.0 (issue #528). - Experimental initialization for Microsoft Windows. - :mod:`rpy2.situation` is now also reporting the rpy2 version. - :func:`rpy2.robjecs.package_utils.default_symbol_check_after` was renamed :func:`rpy2.robjecs.package_utils.default_symbol_resolve`. The named parameters `default_symbol_check_after` present in few methods in :mod:`rpy2.robjects.packages` and :mod:`rpy2.robjects.functions` were modified to keep a consistent naming. - Trying to instantiate an :class:`rpy2.rlike.container.OrdDict` with a a :class:`dict` will result in a :class:`TypeError` rather than a :class:`ValueError`. - Methods of :class:`rpy2.rlike.container.OrdDict` now raises a :class:`NotImplementedError` when not implemented. - The creation of R vectors from Python sequences is now relying on a method :meth:`_populate_r_vector` that allows vectorized implementation to to improve speed. - Continuous integration tests run against Python 3.6, 3.7, and 3.8. It is no longer checked against Python 3.5. Bugs fixed ---------- - `aes` in :mod:`rpy2.robjects.lib.ggplot2` had stopped working with the R package ggplot2 reaching version 3.2.0. (issue #562). - Better handling of recent :mod:`pandas` arrays with missing values (related to issue #544). - The mapping of the R operator `%in%` reachable through the attribute `ro` of R vectors was always returning `True`. It is now working properly. - R POSIXct vectors with `NA` dates were triggering an error when converted in a data frame converted to :mod:`pandas` (issue #561). Release 3.0.5 ============= Bugs fixed ---------- - No longer allow installation if Python 3 but < 3.5. - Fixed error `undefined symbol: DATAPTR` if R < 3.5 (issue #565). Release 3.0.4 ============= Bugs fixed ---------- - Fixed conversion of `pandas` :class:`Series` of dtype `pandas.Int32Dtype`, or `pandas.Int64Dtype` (issue #544). Release 3.0.3 ============= Bugs fixed ---------- - Fixed the evaluation of R code using the "R magic" was delaying all output to the end of the execution of that code, independently of whether the attribute `cache_display_data` was `True` or `False` (issue #543). - Fixed conversion of :class:`pandas.Series` of `dtype` "object" when all items are either all of the same type or are :obj:`None` (issue #540). Release 3.0.2 ============= Bugs fixed ---------- - Failing to import `pandas` or `numpy` when loading the "R magic" extension for jupyter/ipython was hiding the cause of the error in the `ImportError` exception. - Fallback when an R `POSIXct` vector does not had an attribute `"tzone"` (issue #533). - Callback for console reset was not set during R initialization. - Fixed rternalized function returning rpy2 objects (issue #538). - `--vanilla` is no longer among the default options used to initialize R (issue #534). Release 3.0.1 ============= Bugs fixed ---------- - Script to install R packages for docker image never made it to version control. - Conversion of R arrays/matrices into numpy object trigged a segfault during garbage collection (issue #524). Release 3.0.0 ============= New features ------------ - rpy2 can be installed without a development environment. - Unit tests are now relying on the Python module `pytest`. - :attr:`rpy2.rinterface.NA_Integer` is now only defined when the embedded R is initialized. Changes ------- - complete rewrite of :mod:`rpy2.rinterface`. :mod:`cffi` is now used to interface with the R compiled shared library. This allows ABI calls and removes the need to compile binaries. However, if compilation is available (when installing or preparing pre-compiled binaries) faster implementations of performance bottlenecks will be available. - calling :func:`rpy2.rinterface.endr` multiple times is now only ending R the first time it is called (note: an ended R cannot successfully be re-initialized). - The conversion system in the mod:`rpy2.robjects.conversion` now has only two conversions `py2rpy` and rpy2py`. `py2rpy` tries to convert any Python object into an object rpy2 can use with R and `rpy2py` tries to convert any rpy2 object into a either a non-rpy2 Python object or a mod:`rpy2.robjects` level object. - The method `get` for R environments is now called `find()` to avoid confusion with the method of the same name in Python (:meth:`dict.get`). - :class:`rpy2.robjects.vectors.Vector`, :class:`rpy2.robjects.vectors.Matrix`, and :class:`rpy2.robjects.vectors.Array` can no longer be used to create R arrays of unspecified type. New type-specific classes (for example for vectors :class:`rpy2.robjects.vectors.IntVector`, :class:`rpy2.robjects.vectors.BoolVector`, :class:`rpy2.robjects.vectors.FloatVector`, :class:`rpy2.robjects.vectors.ComplexVector`, or :class:`rpy2.robjects.vectors.StrVector`) should be used instead. - mod:`rpy2.rpy_classic`, an implementation of the `rpy` interface using :mod:`rpy2.rinterface` is no longer available. - :class:`rpy2.robjects.ParsedCode` and :class:`rpy2.robjects.SourceCode` are moved to :class:`rpy2.robjects.packages.ParsedCode` and :class:`rpy2.robjects.packages.SourceCode`. Bugs fixed ---------- - Row names in R data frames were lost when converting to pandas data frames (issue #484). Known issues ------------ - Mismatch between R's POSIXlt `wday` and Python time struct_time's `tm_wday` (issue #523). Release 2.9.6 ============= Bugs fixed ---------- - Latest release of :mod:`pandas` deprecated :meth:`DataFrame.from_items`. (issue #514). - Latest release of :mod:`pandas` requires categories to be a list (not an other sequence). Known issues ------------ - The numpy buffer implemented by R arrays is broken for complex numbers Release 2.9.5 ============= Bugs fixed ---------- - Missing values in pandas :class:`Category` series were creating invalid R factors when converted (issue #493). Release 2.9.4 ============= Bugs fixed ---------- - Fallback for failure to import numpy or pandas is now dissociated from failure to import :mod:`numpy2ri` or :mod:`pandas2ri` (issue #463). - :func:`repr` for R POSIX date/time vectors is now showing a string representation of the date/time rather than the timestamp as a float (issue #467). - The HTML representation of R data frame (the default representation in the Jupyter notebook) was displaying an inconsistent number of rows (found while workin on issue #466). - Handle time zones in timezones in Pandas when converting to R data frames (issue #454). - When exiting the Python process, the R cleanup is now explicitly request to happen before Python's exit. This is preventing possible segfaults the process is terminating (issue #471). - dplyr method `ungroup()` was missing from :class:`rpy2.robjects.lib.dplyr.DataFrame` (issue #473). Release 2.9.3 ============= Bugs fixed ---------- - Delegate finding where is local time zone file to either a user-specified module-level variable `default_timezone` or to the third-party module :mod:`tzlocal` (issue #448). Release 2.9.2 ============= Changes ------- - The pandas converter is converting :class:`pandas.Series` of `dtype` `"O"` to :class:`rpy2.robjects.vectors.StrVector` objects, issueing a warning about it (See issue #421). - The conversion of pandas data frame is now working with columns rather than rows (introduce in bug fix for issue #442 below) and this is expected to result in more efficient conversions. Bugs fixed ---------- - Allow floats in figure sizes for R magic (Pull request #63) - Fixed pickling unpickling of robjects-level instances, regression introduced in fix for issue #432 with release 2.9.1 (issue #443). - Fixed broken unit test for columns of `dtype` `"O"` in `pandas` data frames. - Fixed incorrect conversion of R factors in data frames to columns of integers in pandas data frame (issue #442). Release 2.9.1 ============= Changes ------- - Fixing issue #432 (see Section Bugs fixed below) involved removed the method `__reduce__` previously provided for all rpy2 objects representing R objects. Bugs fixed ---------- - An error when installing with an unsupported R version was fixed (issue #420). - The docstring for `rinterface.endr()` was improperly stating that the function was not taking any argument (issue #423). - Target version of dplyr and tidyr are now 0.7.4 and 0.7.2 respectively. - Fixed memory leak when pickling objects (issue #432). Fixing the leak caused a slight change in the API (see Section Changes above). - Conversion to :mod:`pandas` now handling R ordered factor (issue #398). - :mod:`jinja2` was not listed as a dependency (issue #437). Release 2.9.0 ============= New features ------------ - New module :mod:`rpy2.situation` to extract and report informations about the environment, such as where is the R HOME, what is the version of R, what is the version of R rpy2 was built with, etc... The module is also designed to be run directly and provide diagnostics: `python -m rpy2.situation`. - :meth:`Environment.values`, :meth:`Environment.pop`, :meth:`Environment.popitems`, :meth:`Environment.clear` to match :meth:`dict.values`, :meth:`dict.pop`, :meth:`dict.popitems`, :meth:`dict.clear`. - :class:`VectorOperationsDelegator` now has a method `__matmul__` to implement Python's matrix multiplication operator (PEP-0645). - A rule to convert R POSIXct vectors to pandas Timestamp vectors was added (issue #418). - method :meth:`_repr_html_` for R vectors to display HTML in jupyter. Changes ------- - Starting several times the singleton :class:`EventProcessor` longer results in a :class:`RuntimeError`. This is now only a warning, addressing issue #182. - The target version for the R package `dplyr` mapped is now 0.7.1, and :func:`rpy2.robjects.lib.dplyr.src_dt` (issue #357) and :func:`rpy2.robjects.lib.dplyr.src_desc` are no longer present. - :meth:`Environment.keys` is now a iterator to match :meth:`dict.keys`, also an interator in Python 3. - Target version of `ggplot2` library is 2.2.1. - Option `stringsasfactors` in the constructor for the class `DataFrame`. If `False`, the strings are no longer converted to factors. When converting from pandas data frames the default is to no longer convert columns of strings to factors. - The R "magic" for jupyter is now more consistently using the conversion system, and the use of custom converters through the magic argument `-c` will work as expected. - Docker-related files moved to directory docker/ (where variants image for rpy2 are available) Bugs fixed ---------- - :func:`numpy.float128` is not available on all platforms. The unit test for it is now skipped on systems where it is not present (issue #347) - R pairlist objects can now be sliced (and issue #380 is resolved). - Passing parameters names that are empty string to R function was causing a segfault (issue #409). - Trying to build an atomic R vector from a Python object that has a length, but it not a sequence nor an iterator was causing a segfault (issue #407). Release 2.8.6 ============= Bugs fixed ---------- - Trying to build an atomic R vector from a Python object that has a length, but it not a sequence nor an iterator was causing a segfault (issue #407 - backport from rpy2-2.9.0). Release 2.8.5 ============= Bugs fixed ---------- - The defintion of the method :class:`rpy2.rlike.container.OrdDict.items` was incorrect, and so was the documentation for `rcall` (issue #383) - Giving an empty sequence to :meth:`robjects.sequence_to_vector` is now raising a :class:`ValueError` rather than fail with an :class:`UnboundLocalError` (see issue #388). - :meth:`robjects.robject.RSlots.items` is now working (see pull request #57). Release 2.8.4 ============= Bugs fixed ---------- - The context manager :func:`rpy2.robjects.lib.grdevices.render_to_file` is no longer trying to impose a file name generated by :mod:`tempfile` (issue #371) - The symbol `LISTSXP` (corresponding to R pairlist objects) was not imported from the C module in rpy2. - The functions `scale_linetype_discrete` and `scale_linetype_continuous` in ggplot2 were not wrapped by :mod:`rpy2.robjects.lib.ggplot2` (issue #381) Release 2.8.3 ============= Bugs fixed ---------- - Fixed the error when the transformation of R "man" pages into Python docstrings was failing when the section "arguments" was missing (issue #368) - Failing to find R in the PATH during the installation of rpy2 is now printing an error message instead of a warning (issue #366) Release 2.8.2 ============= Bugs fixed ---------- - R's `dplyr::src_dt` was moved to `dtdplyr::src_dt` with `dplyr` release 0.5.0. To address this, `src_dt` will become a `None` if the R package `dplyr` is discovered to be of version >= 0.5.0 at runtime. (issue #357) - Conversion issue when R symbols were accessed as attribute of the singleton :class:`rpy2.robjects.R`. (issue #334) - The `rmagic` extension for `ipython` was no longer loading with the latest ipython (version 5.0.0). (issue #359) Changes ------- - The fix to issue #357 (see bugs fixed above) was expanded to cover all R packages wrapped in :mod:`rpy2.robjects.lib` and ensure that the respective Python modules can loaded even if symbols are no longer defined in future versions of the corresponding R packages. Release 2.8.1 ============= New features ------------ - `Dockerfile` with automated build on dockerhub (https://hub.docker.com/r/rpy2/rpy2) Bugs Fixed ---------- - Trying to install rpy2 while R is not in the `PATH` resulted in an error in `setup.py`. Release 2.8.0 ============= New features ------------ - New class :class:`rpy2.robjects.SourceCode`. The class extends Python's :class:`str` and is meant to represent R source code. An HTML renderer for the ipython notebook (syntax highlighting using :mod:`pygment` is also added). - New module :mod:`rpy2.robjects.lib.tidyr` providing a custom wrapper for the R library `tidyr` - The long-deprecated functions :func:`rpy2.rinterface.set_writeconsole` and :func:`rpy2.rinterface.get_writeconsole` are no longer available. One of :func:`rpy2.rinterface.set_writeconsole_regular` / :func:`rpy2.rinterface.set_writeconsole_warnerror` or :func:`rpy2.rinterface.get_writeconsole_regular` / :func:`rpy2.rinterface.get_writeconsole_warnerror` respectively should be used instead. - The attribute :attr:`rpy2.robjects.RObject.slots` can now be implictly interated on (the method :meth:`__iter__` is now an alias for :meth:`keys`). - The default Python-R conversion is now handling functions. This means that Python function can directly be used as parameters to R functions (when relevant). - Ipython display hook `display_png` for ggplot2 graphics. - :mod:`pandas` "category" vectors are better handled by the pandas conversion. - New module :mod:`rpy2.robjects.lib.grdevices` providing a custom wrapper for the R library 'grDevices', exposing few key functions in the package and providing context managers (`render_to_file` and `render_to_bytesio`) designed to simplify the handling of static plots (e.g., webserver producing graphics on the fly or figure embedded in a Jupyter notebook). - Numpy conversion is handling better arrays with `dtype` equal to `"O"` when all objects are either all inheriting from :class:`str` or from :class:`bytes`. Such arrays are now producing :class:`StrSexpVector` or :class:`BytesSexpVector` objects respectively. - R's own printing of warnings if now transformed to warnings of type `rinterface.RRuntimeWarning` (it used to be a regular `UserWarning`) - The family of functions `src_*` and the function `tbl` in the R package `dplyr` have aliases in the module :mod:`rpy2.robjects.lib.dplyr`, and a class :class:`DataSource` has been added for convenience. - :class:`rpy2.robjects.vectors.DataFrame` has a method `head` corresponding to R's method of the same name. The method takes the n first row of a data frame. - dplyr's functions `count_` and `tally` are now exposed as methods for the class :class:`dplyr.DataFrame`. Changes ------- - Building/installing rpy2 with a development version of R does not require the use of option `--ignore-check-rversion` any longer. A warning is simply issue when the R version is "development". - On MSWindows, the dependency on `pywin32` was removed (issue #315) - :class:`GroupedDataFrame` in the dplyr interface module is now inheriting from the definition of DataFrame in that same module (it was previously inheriting from :class:`robjects.vectors.DataFrame`). - The default `repr()` for R objects is now printing the R classes (as suggested in issue #349). Bugs Fixed ---------- - Parameter names to R function that are in UTF-8 are no longer causing a segfault (issue #332) - Looking for a missing key in an R environment (using `__getitem__` or `[`) could raise a `LookupError` instead of a `KeyError`. - R environment can now handle unicode keys as UTF-8 (was previously trying Latin1) - rpy2 is interrupting attempts to install with Python < 2.7 with an informative error message (issue #338) - Setting the R class can be done by using a simple Python string (issue #341) - `rpy2.robjects.lib.grid.viewport` is now returning an instance of class `Viewport` (defined in the same module) (issue #350) Release 2.7.9 ============= Bug fixed --------- - Python objects exposed to R could lead to segfault when the Python process is exiting (issue #331) Release 2.7.8 ============= Bugs fixed ---------- - American English spelling was missing for some of the function names to specify colour (color) scales. - Fix for printing R objects on Windows (pull request #47) Release 2.7.7 ============= Bugs fixed ---------- - Pickling `robjects`-level objects resulted in `rinterface`-level objects when unpickled (issue #324). Release 2.7.6 ============= Changes ------- - :mod:`rpy2.robjects.lib.ggplot2` was modified to match the newly released ggplot2-2.0.0. This is introducing API-breaking changes, which breaks the promise to keep the API stable through bugfix releases within series, but without it 2.7.x will not a work with new default installation of the R package ggplot2. Release 2.7.5 ============= Bugs fixed ---------- - Division and floordivision through the delegator `.ro` provided with R vectors wrapped by `robjects`. (issue #320) - Memory leak when unserializing (unpickling) R objects bundled in Python objects (issue #321) Release 2.7.4 ============= Bugs fixed ---------- - Python 3.5 highlighted slightly incorrect C-level flags in rpy2 objects declarations, and :mod:`rpy2.robjects` could not be imported. - Fixed unit tests for rmagic when :mod:`numpy` is not installed, and for :mod:`numpy` is installed by :mod:`pandas` in missing. Release 2.7.3 ============= Bugs fixed ---------- - method :meth:`DataFrame.collect` in :mod:`rpy2.robjects.lib.dplyr` was not functioning. - Applied patch by Matthias Klose to fix implict pointer conversions. - :mod:`pandas2ri.ri2py_dataframe` is now propagating the row names in the R data frame into an index in the pandas data frame (issue #285) - methods `union`, `intersect`, `setdiff`, `ungroup` defined in the R package `dplyr` were missing from the `DataFrame` definition in :mod:`rpy2.robjects.lib.dplyr` Release 2.7.2 ============= Bugs fixed ---------- - methods `distinct`, `sample_n`, and `sample_frac` defined in the R package `dplyr` were missing from the `DataFrame` definition in :mod:`rpy2.robjects.lib.dplyr` - The fix for the inheritance problem with :mod:`rpy2.robjects.lib.dplyr.DataFrame` introduced a regression whenever `group_by` is used. - The methods to perform joins on dplyr `DataFrame` objects where not working properly. Release 2.7.1 ============= Bugs fixed ---------- - The :meth:`__repr__` for :mod:`robjects`-level vectors was broken for vectors of length 1 (issue #306) - The ipython notebook-based sections of the documentation were not building - Classes inheriting from :mod:`dplyr.DataFrame` had dplyr methods returning objects of their parent class. Release 2.7.0 ============= New features ------------ - New exception :class:`rpy2.rinterface.RParsingError`. Errors occurring when parsing R code through :func:`rpy2.rinterface.parse` raise this exception (previously :class:`rpy2.rinterface.RRuntimeError`). - New class :class:`rpy2.robjects.conversion.Converter` to replace the `namedtuple` of the same name - New class :class:`rpy2.robjects.converter.ConversionContext`. This is a context manager allowing an easy setting of local conversion rules. The constructor has an alias called :meth:`rpy2.robjects.constructor.localconverter`. - New module :mod:`rpy2.robjects.lib.dplyr` providing a custom wrapper for the R library `dplyr` - Method :meth:`Environment.items()` to iterate through the symbols and associated objects in an R environment. - Exception :class:`rpy2.rinterface.ParsingIncompleError`, a child class of :class:`rpy2.rinterface.ParsingError`, raised when calling :meth:`rpy2.rinteface.parse` results in R's C-level status to be `PARSE_INCOMPLETE`. This can make the Python implementation of an IDE for R easier. - Attribute :attr:`slots` for :mod:`rpy2.robjects`-level objects. The attribute is a :class:`rpy2.robjects.Rslots` which behaves like a Python mapping to provide access to R-attributes for the object (see issue #275). - The R "magic" for ipython `%%R` can be passed a local converter (see new features above) by using `-c`. Bugs fixed ---------- - Conversion rules were not applied when parsing and evaluating string as R with :class:`rpy2.robjects.R`. - Calling the constructor for :class:`rpy2.robjects.vectors.FactorVector` with an R factor is no longer making a copy, loosing the associated R attributes if any (fixes issue #299). - `rpy2` could crash when R was unable to dynamically load the C extension for one of its packages (noticed with issue #303). Changes ------- - :func:`rpy2.rinterface.is_initialized` is now a function. - :meth:`rpy2.robjects.R.__call__` is now calling R's `base::parse()` to parse the string rather the parser through R's C-API. The workaround let's us retrieve R's error message in case of failure (see issue #300) Release 2.6.3 ============= Bug fixed --------- - Metaclass `RS4Auto_Type` facilitating the creation of Python classes from R S4 classes was not handling classes without methods (issue #301) Release 2.6.2 ============= Bugs fixed ---------- - Check that R >= 3.2 is used at build time (issue #291) - Conversion rules were not applied when parsing and evaluating string as R code with :class:`rpy2.robjects.R`. Release 2.6.1 ============= New features ------------ - Because of their long names, the classes :class:`SignatureTranslatedAnonymousPackage`, :class:`SignatureTranslatedPackage`, and :class:`SignatureTranslatedFunction` in :mod:`rpy2.robjects.packages` have now the aliases :class:`STAP`, :class:`STP`, and :class:`STF` respectively. Bugs fixed ---------- - Typo in function name emitting warnings at build time (issue #283) - The conversion of `TaggedList` instances is now handling the names of items in the list (issue #286) Changes ------- - Loading the `ipython` extension in the absence of `pandas` or `numpy` is now issuing a warning (issue #279) Release 2.6.0 ============= New features ------------ - Report the existence during build time of a file `.Renviron`, or the definition of the environment variables `R_ENVIRON' or `R_ENVIRON_USER` with a warning. (issue #204) - Moved console writting callback to use `ptr_R_WriteConsoleEx` rather than `ptr_R_WriteConsole`. This allows callbacks for warnings and messages. `get/set_writeconsole` is now replaced by `get/set_writeconsole_regular` (regular output) and `get/set_writeconsole_warnerror` (warning and error). In order to conserve backward compatibility an alias for `get/set_writeconsole_regular` called `get/set_writeconsole` is provided. - Added callback for `ptr_R_ResetConsole`. - :mod:`pandas` :class:`Categorical` objects are automatically handled in the pandas converter. - The translation of R symbols into Python symbols used in `importr` and underlying classes and methods can be customized with a callback. The default translation turning `.` into `_` is `default_symbol_r2python`. - Translation of named arguments in R function is now sharing code with the translation of R symbols (see point above), providing a consistent way to perform translations. - Utility function `sequence_to_vector` in `robjects` to convert Python sequences (e.g., `list` or `tuple`) to R vector without having to specify the type (the type is inferred from the list). - :mod:`robjects.vectors` object have a property :attr:`NAvalue` that contains the `NA` value for the vector, allowing generic code on R vectors. For example, testing whether any vector contains `NA` can be written as `any(x is myvector.NAvalue for x in myvector)`. Making numpy /masked/ array is an other application. Changes ------- - The automatic name translation from R to Python used in `importr` is now slightly more complex. It will not only translate `.` to `_` but should a conflict arise from the existence in R of both the `.` and `_` versions the `.` version will be appended a `_` (in accordance with :pep:0008). The change was discussed in issue #274). - The ipython 'R magic' is now starting with a default conversion mode that is `pandas2ri` if it can find it, then `numpy2ri` if it can find it, and then the basic conversion. - R vectors are now typed at the C level (IntSexpVector, FloatSexpVector, ListSexpVector, etc...) whenever retrieving them from the embedded R with the low-level `rinterface`. This is facilitating dispatch on vector type (e.g., with `singledispatch` now used for the conversion system). Bugs fixed ---------- - The evaluation of R code through R's C-level function `tryEval` caused console output whenever an error occurred. Moving to the seemingly experimental `tryEvalSilent` makes evaluations less verbose. - Multiple plots in one ipython cell (pull request #44) Release 2.5.7 ============= - `simplegeneric` was moved of ipython 4.0.0 (pull request #43) Release 2.5.6 ============= Bugs fixed ---------- - Detection of the R version during setup on Win8 (issues #255 and #258) - Segmentation fault when converting :mod:`pandas` :class:`Series` with elements of type object (issue #264) - The default converter from Python (non-rpy2) objects to rinterface-level objects was producing robjects-level objects whenever the input was of type :class:`list` (discovered while fixing issue #264) - Implemented suggested fix for issue with unlinking files on Windows (issue #191) - Testing rpy2 in the absence of ipython no longer stops with an error (issue #266) Release 2.5.5 ============= Bugs fixed ---------- - Crash (segfault) when querying an R object in an R environment triggers an error (symbol exists, but associated values resolves to an error - issue #251) - Change in the signature of `rcall` was not updated in the documentation (issue #259) - Minor update to the documentation (issue #257) Release 2.5.4 ============= Bugs fixed ---------- - Filter PNG files on size, preventing empty files causing trouble to be ipython notebook rendering of graphics later on (slight modification of the pull request #39) - Fix installation left unresolved with rpy2-2.5.3 (issue #248) - Possible segfault with Python 3.4 (issue #249) Release 2.5.3 ============= Changes ------- - `setup.py` has `install_requires` in addition to `requires` in the hope to fix the missing dependency with Python 2 (:mod:`singledispatch` is required but not installed). Bugs fixed ---------- - Extracting configuration information from should now work when R is emitting a warning (issue #247) - On OS X the library discovery step can yield nothing (see issue #246). A tentative fix is to issue a warning and keep moving. Release 2.5.2 ============= Bugs fixed ---------- - String representation of :class:`robjects.R` (issue #238) - Check during `build_ext` if unsupported version of R (pull request #32) - HTMl display of columns of factors in a DataFrame (issue #236) - HTML display of factors (issue #242) Release 2.5.1 ============= Bugs fixed ---------- - Require singledispatch if Python 3.3 (issue #232) - Fixed bug when R spits out a warning when asked configuration information (issue #233) - Restored printing of compilation information when running `setup.py` - Fixed installation issue on some systems (issue #234) - Workaround obscure failure message from unittest if Python < 3.4 and :mod:`singledispatch` cannot be imported (issue #235) Release 2.5.0 ============= New features ------------ - Experimental alternative way to preserve R objects from garbage collection. This can be activated with `rinterface.initr(r_preservehash=True)` (default is `False`. - :class:`GGPlot` object getting a method :meth:`save` mirroring R's `ggplot2::ggsave()`. - The conversion system is now using generics/single dispatch. - New module :mod:`rpy2.ipython.html` with HTML display for rpy2 objects - [Experimental] New function :func:`robjects.methods.rs4instance_factory` to type RS4 objects with more specificity. Changes ------- - The script `setup.py` was rewritten for clarity and ease of maintenance. Now it only uses `setuptools`. Release 2.4.4 ============= Bugs fixed ---------- - Use `input` rather than `raw_input` in the default console callback with Python 3 (fixes issue #222) - Issues with conversions, pandas, and rmagic (fixes issue #218 and more) Release 2.4.3 ============= Bugs fixed ---------- - `geom_raster` was missing from `rpy2.robjects.lib.ggplot2` (pull request #30) - Fixed issue with SVG rendering in ipython notebook (issue #217) - Regression with `rx2()` introduced with new conversion (issue #219) - Fixed documentation (missing `import`) (issue #213) Release 2.4.2 ============= Bugs fixed ---------- - Assigning an R `DataFrame` into an environment was failing if the conversion for Pandas was activated. (Issue #207) Release 2.4.1 ============= Bugs fixed ---------- - :meth:`rpy2.ipython` fixed spurious output to notebook cells. Release 2.4.0 ============= Changes ------- - Conversion system slightly changed, with the optional conversions for :mod:`numpy` and :mod:`pandas` modified accordingly. The changes should only matter if using third-party conversion functions. - The Python 3 version is now a first class citizen. `2to3` is no longer used, and the code base is made directly compatible with Python. This lowers significantly the installation time with Python 3 (which matters when developping rpy2). - The default options to initialize R (`rpy2.rinterface.initoptions') are no longer `('rpy2', '--quiet', '--vanilla', '--no-save')` but now `('rpy2', '--quiet', '--no-save')`. - :class:`robjects.vectors.ListVector` can be instanciated from any objects with a method `items()` with the expectation that the method returns an iterable of (name, value) tuples, or even be an iterable of (name, value) tuples. New features ------------ - For instances of :class:`rpy2.robjects.Function`, the `__doc__` is now a property fetching information about the parameters in the R signature. - Convenience function :func:`rpy2.robjects.packages.data` to extract the datasets in an R pacakges - :mod:`ipython`'s `rmagic` is now part of :mod:`rpy`. To use, `%load_ext rpy2.ipython` from within IPython. - new method :meth:`rpy2.rinterface.SexpEnvironment.keys`, returnings the names in the environment as a tuple of Python strings. - convenience class :class:`robjects.packages.InstalledPackages`, with a companion function :func:`robjects.packages.isinstalled`. - new class :class:`rinterface.SexpSymbol` to represent R symbols Bugs fixed ---------- - :meth:`rpy2.rinterface.Sexp.do_slot` was crashing when the parameter was an empty string (PR #155) Release 2.3.10 ============== Bugs fixed ---------- - `setup.py build` was broken when new R compiled with OpenMP (Issue #183) Release 2.3.9 ============= - Changes in pandas 0.13.0 broke the rpy2 conversion layer (Issue #173) Release 2.3.8 ============= Bugs fixed ---------- - Crash with R-3.0.2. Changes in R-3.0.2's C API coupled to a strange behaviour with R promises caused the problem. (PR #150) Release 2.3.7 ============= Bugs fixed ---------- - ggplot2's "guides" were missing - ggplot2's "theme_classic" was missing (PR #143) - ggplot2's "element_rect" was missing (PR #144) - :func:`rpy2.interactive.packages` was broken (PR #142) Release 2.3.6 ============= Bugs fixed ---------- - Several reports of segfault on OS X (since rpy2-2.3.1 - PR #109) - More fixes in converting `DataFrames` with dates from `pandas` Relase 2.3.5 ============ Bugs fixed ---------- - Missing mapping to ggplot2's `scale_shape_discrete` function - Better handling of dates in Pandas - Constructor for POSIXct improved (and fixed) Changes ------- - The attribute :attr:`rclass` is no longer read-only and can be set (since R allows it) - Importing the module :mod:`rpy2.interactive` no longer activates event processing by default (triggering concurrency errors when used with ipython). New features ------------ - New module :mod:`rpy2.interactive.ipython` (so far plotting automatically a ggplot2 figure in the iPython's console) - It is now possible to set the :attr:`rclass`. Relase 2.3.4 ============ Bugs fixed ---------- - Spurious error when running unit tests with Python 3 and numpy installed - Missing mapping to ggplot2's `geom_dotplot` function - Warnings are not longer printed (see Changes below) Changes ------- - Bumped target version of ggplot2 to 0.9.3.1 - Warnings are not longer printed. The C-level function in R became hidden in R-3.0, and the cost of an R-level check/print is relatively high if the R code called is very short. This might evolve into printing warnings only if interactive mode in Python (if this can be checked reliably). Release 2.3.3 ============= Bugs fixed ---------- - Some of the data.frames converted from :mod:`pandas` were triggering a :class:`TypeError` when calling :func:`repr` - In :mod:`rpy2.robjects.lib.ggplot2`, a mapping to `coord_fixed` was missing (PR #120) - Using the parameter `lib_loc` in a call to :func:`rpy2.robjects.packages.importr` was resulting in an error (PR #119) - Creating a `layer` through the `rpy2.robjects.lib.ggplot2` interface did not accept parameters (PR #122) - Testing the Python version was crashing of a number of unsupported Python versions (<= 2.6) (PR #117) New features ------------ - New module pandas2ri to convert from mod:`pandas` `DataFrame` objects - New classes :class:`rpy2.robjects.lib.grid.Unit` and :class:`rpy2.robjects.lib.grid.Gpar` to model their counterparts in R's `grid` package as they were previously missing from rpy2. Release 2.3.2 ============= Bug fixed --------- - Building on Win64 (pull request #6) - Fetching data from an R package through `importr` was masking any R object called `data` in that package. The data are now under the attribute name `__rdata__`. This is not completely safe either, although much less likely, a warning will be issued if still masking anything. Changes ------- - More informative error message when failing to build because `R CMD config` does not return what is expected Release 2.3.1 ============= Bugs fixed ---------- - default console print callback with Python (issue #112 linked to it) - deprecation warnings with ggplot2 (issue #111 and contributed patch) Release 2.3.0 ============= New Features ------------ :mod:`rpy2.rinterface`: - C-level API, allowing other C-level modules to make use of utilities without going through the Python level. The exact definition of the API is not yet fixed. For now there is PyRinteractive_IsInitialized() to assess whether R was initialized (through :mod:`rpy2.rinterface` or not). - C-module _rpy_device, allowing one to implement R graphical devices in Python [(very) experimental] - Tracking of R objects kept protected from garbage collection by rpy2 is now possible. - New method :meth:`Sexp.rid` to return the identifier of the R object represented by a Python/rpy2 object :mod:`rpy2.rinteractive`: - Dynamic build of Python docstrings out of the R manual pages :mod:`rpy2.robjects.help`: - Build dynamic help :mod:`rpy2.robjects.packages`: - Build anonymous R packages from strings - When using :func:`importr`, the datasets are added as an attribute :attr:`data`, itself an instance of a new class :class:`PackageData`. It no longer possible to access datasets are regular objects from a code package (because of changes in R), and the new system is more robust against quirks. Changes ------- :mod:`rpy2.rinterface`: - :attr:`SexpClosure.env` to replace the method `closureenv`. Release 2.2.6 ============= Bugs fixed ---------- - Newest R-2.15 and ggplot2 0.9 broke the ggplot2 interaface in :mod:`rpy2.robjects.lib.ggplot2` Release 2.2.5 ============= Bugs fixed ---------- - install process: Library location for some of the R installations - should compile on win32 (thanks to a patch from Evgeny Cherkashin), a work to a limited extend Release 2.2.4 ============= Bugs fixed ---------- - Memory leak when creating R vectors from Python (issue #82) Release 2.2.3 ============= Bugs fixed ---------- - Dynamic construction of S4 classes was looking for R help as 'class.' rather than '-class' - The cleanup of temporary directories created by R was not happening if the Python process terminated without calline :func:`rpy2.rinterface.endr()` (issue #68, and proof-of-principle fix by chrish42) Release 2.2.2 ============= Bugs fixed ---------- - With the robjects layer, repr() on a list containing non-vector elements was failing Release 2.2.1 ============= Bugs fixed ---------- - MANIFEST.in was missing from MANIFEST.in, required with Python 3 Release 2.2.0 ============= New Features ------------ - Support for Python 3, and for some of its features ported to Python 2.7 :mod:`rpy2.robjects`: - :meth:`Environment.keys` to list the keys - classes :class:`robjects.vectors.POSIXlt` and :class:`robjects.vectors.POSIXlt` to represent vectors of R dates/time - :func:`packages.get_packagepath` to get the path to an R package - module :mod:`rpy2.robjects.help` to expose the R help system to Python - Metaclass utilities in :mod:`rpy2.robjects.methods`, allowing to reflect automatically R S4 classes as Python classes. - :meth:`rpy2.robjects.vectors.FactorVector.iter_labels` to iterate over the labels - :class:`rpy2.robjects.vectors.ListVector` to represent R lists. - Constructor for :class:`rpy2.robjects.vectors.ListVector` and :class:`rpy2.robjects.vectors.DataFrame` accept any iterable at the condition that the elements iterated through also valid subscripts for it (e.g., given an iterable v, the following is valid: .. code-block:: python x[k] for x in v :mod:`rpy2.rinterface`: - :data:`NA_Complex` and :class:`NAComplexType` for missing complex values. - :class:`SexpExtPtr` to represent R objects of type EXTPTR (external pointers). - :func:`rpy2.rinterface.parse` to parse a string a R code - :func:`rpy2.rinterface.rternalise` to wrap Python function as :class:`SexpClosure` that can be called by R just as it was a function of its own. - :class:`rpy2.rinterface.RNULLType` for R's C-level NULL value and :class:`rpy2.rinterface.UnboundValueType` for R's C-level R_UnboundValue (both singletons). - :meth:`rinterface.SexpVector.index`, of similar behaviour to :meth:`list.index`. - :meth:`rpy2.rinterface.Sexp.list_attrs` to list the names of all R attributes for a given object. - :class:`rpy2.rinterface.ByteSexpVector` to represent R 'raw' vectors. - constant `R_LEN_T_MAX` to store what is the maximum length for a vector in R. - tuple `R_VERSION_BUILD` to store the version of R rpy2 was built against - getter :attr:`Sexp.rclass` to return the R class associated with an object :mod:`rpy2.rlike`: - :class:`container.OrdDict` get proper methods :meth:`keys` and `get` :mod:`rpy2.interactive`: - A new sub-package to provide utilities for interactive work, either for handling R interactive events or use Python for interactive programming (as often done with the R console) Changes ------- :mod:`rpy2.robjects`: - NA_bool, NA_real, NA_integer, NA_character and NA_complex are now deprecated (and removed). NA_Logical, NA_Real, NA_Integer, NA_Character, NA_Complex should be used. - :class:`rpy2.robjects.packages.Package` now inherits from :class:`types.ModuleType` - classes representing R vector also inherit their type-specific rinterface-level counterpart. - Importing the :class:`rpy2.robjects.numpy2ri` is no longer sufficient to active the conversion. Explicit activation is now needed; the function `activate` can do that. :mod:`rpy2.rinterface`: - :class:`IntSexpVector`, :class:`FloatSexpVector`, :class:`StrSexpVector`, :class:`BoolSexpVector`, :class:`ComplexSexpVector` are now defined at the C level, improving performances and memory footprint whenever a lot of instances are created. Bugs fixed ---------- - Better and more explicit detection system for needed libraries when compiling rpy2 (ported to release 2.1.6) - Long-standing issue with readline fixed (issue #10) Release 2.1.9 ============= Bugs fixed ---------- - The R class in rpy2.robjects is now truly a singleton - When using numpy 1.5 and Python >= 2.7, the exposed buffer for R numerical (double) vectors or arrays was wrong. Release 2.1.8 ============= Bugs fixed ---------- - Fixed issue with R arrays with more than 2 dimensions and numpy arrays (issue #47 - backported from the branch 2.2.x). Release 2.1.7 ============= Bugs fixed ---------- - More fixes for the automated detection of include and libraries at build time. Release 2.1.6 ============= Bugs fixed ---------- - Further fixes in the automatic detection of includes and libraries needed to compile rpy2 against R. The detection code has been refactored (backport from the 2.2.x branch) Release 2.1.5 ============= Bugs fixed ---------- - fixes the automatic detection of R_HOME/lib during building/compiling when R_HOME/lib is not in lib/ (issue #54) Release 2.1.4 ============= New features ------------ - :mod:`rpy2.robjects.lib.ggplot2` now has the functions :func:`limits`, :func:`xlim`, :func:`ylim` exposed (patch contributed anonymously) Bugs fixed ---------- - Install script when the BLAS library used by R is specified as a library file (patch by Michael Kuhn) Release 2.1.3 ============= Bugs fixed ---------- - Spurious error message when using DataFrame.from_csvfile() without specifying col_names or row_names - Patch to finally compile with Python < 2.6 (contribDuted by Denis Barbier) Release 2.1.2 ============= New Features ------------ :mod:`rpy2.robjects`: - NA_Logical, NA_Real, NA_Integer, NA_Character from :mod:`rpy2.rinterface` are imported by robjects. Changes ------- :mod:`rpy2.robjects`: - NA_bool, NA_real, NA_integer, NA_character and NA_complex are now robjects-level vectors (they were rinterface-level vectors). Consider using the rinterface-defined NAs instead of them. Bugs fixed ---------- - Missing conditional C definition to compile with Python 2.4 # issue 38 - Fixed error when calling robjects.vectors.Vector.iteritems() on an R vector without names - Fixed automatic conversion issues (issue #41) Release 2.1.1 ============= Bugs fixed ---------- - Issues with NA values # issue 37 - Missing manual scale functions in :mod:`rpy2.robjects.lib.ggplot2` # issue 39 Release 2.1.0 ============= New Features ------------ :mod:`rpy2.robjects`: - Method :meth:`formals` for :class:`Function` (formerly *RFunction*) - Methods :meth:`slotnames`, :meth:`isclass`, and :meth:`validobject` for :class:`RS4` - Vector-like objects now in a module :mod:`rpy2.robjects.vectors` - :func:`set_accessors` for adding simply accessors to a class inheriting from :class:`RS4` - :class:`RS4_Type` for metaclass-declared accessors - Delegating classes :class:`ExtractDelegator` and :class:`DoubleExtractDelegator` for extracting the R-way - :class:`DataFrame` (formerly *RDataFrame*) can now be created from :`rlike.container.OrdDict` instances, or any other object inheriting from dict. - :class:`FactorVector` to represent R factors - the conversion is now returning subclasses of :class:`robjects.vectors.Vector` -formerly *RVector*- (such as :class:`IntVector`, :class:`FloatVector`, etc...) rather than only return :class:`Vector` - :class:`StrVector` has a method :meth:`factor` to turn a vector of strings into an R factor - :class:`Matrix` was added the methods: :meth:`dot`, :meth:`svd`, :meth:`crossprod`, :meth:`tcrossprod`, :meth:`transpose`. - :meth:`IntVector.tabulate` to count the number of times a value is found in the vector - :meth:`Vector.sample` to draw a (random) sample of arbitrary size from a vector - :data:`NA_Bool`, :data:`NA_Real`, :data:`NA_Integer`, :data:`NA_Character`, :data:`NA_Complex` as aliases for R's missing values. - :data:`ComplexVector` for vectors of complex (real + imaginary) elements - :mod:`packages` to provide utility functions to handle R packages (import of R packages) - :mod:`functions` to provide classes related to R functions, with the new class :class:`SignatureTranslatedFunction` - :meth:`DataFrame.iter_row` and :meth:`DataFrame.iter_column`, iterating through rows and columns respectively. - :meth:`DataFrame.cbind` and :meth:`DataFrame.rbind` for binding columns or rows to a DataFrame. - :meth:`Vector.iteritems` to iterate on pairs of names and values. - :attr:`Robject.__rname__` to store the "R name" :mod:`rpy2.rinterface`: - New functions for specifying callback functions for R's front-ends: :func:`set_showmessage`, :func:`set_flushconsole`, :func:`set_choosefile`, :func:`set_showfiles` - New object :data:`MissingArg`, exposing R's special object for representing a "missing" parameter in a function call. (#this was first a patch by Nathaniel Smith with a function getMissingArgSexp) - Initial commit of a callback-based implementation of an R graphical device (this is for the moment very experimental - and not fully working) - :meth:`SexpClosure.rcall` is now taking 2 parameters, a tuple with the parameters and an :class:`SexpEnvironment` in which the call is to be evaluated. - :attr:`Sexp.__sexp__` now has a setter method. This permits the rebinding of the underlying R SEXP, and allows to expose `foo<-` type of R methods as Python function/methods with side effects. - Objects of R type RAWSXP are now exposed as instances of class :class:`SexpVector`. - Factory function :func:`unserialize` to build Sexp* instances from byte string serialized with R's own 'serialize'. - Method :meth:`Sexp.__reduce__` for pickling/unpickling - Ability to specify a callback function for R_CleanUp (called upon exiting R) through :func:`get_cleanup` and :func:`set_cleanup` [very experimental] - Class :class:`ListSexpVector` for creating R lists easily (complementing :class:`IntSexpVector`, :class:`StrSexpVector`, and friends) - :meth:`colnames`, :meth:`rownames` for :class:`Array` (formerly *RArray*) are now property-style getters - Pairlists (LISTSXP) now handled - Experimental function :func:`set_interactive` to set whether R is in interactive mode or not (#following an issue reported by Yaroslav Halchenko) - New object :data:`R_NilValue`, exposing R's special object for representing a "NULL". - :data:`ComplexSexpVector` for vectors of complex (real + imaginary) elements - Scalar Python parameters of type :class:`int`, :class:`long`, :class:`double`, :class:`bool`, and :class:`None` in a call (using :class:`SexpClosure`) are now automatically converted to length-one R vectors (at the exception of None, converted to R_NilValue). - Python slices can now be used on R vector-like objects - Better handling of R's missing values NA, `NA_integer_`, `NA_real_`, and `NA_character_`. :mod:`rpy2.rlike`: - :meth:`iteritems` for :class:`OrdDict` (formerly:class:`ArgDict`) and :class:`TaggedList` - static method :meth:`from_iteritems` for :class:`TaggedList`, for creating a TaggedList from any object having a method :meth:`iteritems` Changes ------- - The setup.py script is now taking command-line arguments when specifying R library-related paths are wished. python setup.py --help build_ext will list them :mod:`rpy2.robjects`: - RS4 no longer makes R's slots as Python attributes through :meth:`__attr__` - The package is split into modules - The broken variables NA_STRING, NA_INTEGER, NA_LOGICAL, and NA_REAL are removed. The documentation on missing values was revised. - :data:`globalEnv` and :data:`baseNameSpaceEnv` were renamed to :data:`globalenv` and :data:`baseenv` respectively - The parameter *wantFun* in :meth:`Environment.get` (formerly *REnvironment.get()*) is now *wantfun* - :attr:`Vector.r` does not have a __getitem__ method any longer (see in `.rx` and `.rx2` in the new features) - :meth:`colnames`, :meth:`rownames`, :meth:`nrow`, :meth:`ncol` for :class:`DataFrame` are now property-style getters - :meth:`nrow`, :meth:`ncol` for :class:`Array` are now property-style getters - static method :meth:`from_csvfile` and instance method :meth:`to_csvfile` for :class:`DataFrame` - module :mod:`lib` to store modules representing R packages - module :mod:`lib.ggplot2` for the CRAN package ggplot2. - renaming of few classes, the *R* prefix: :class:`Formula` (from *RFormula*), :class:`DataFrame` (from *RDataFrame*), :class:`Array` (from *RArray*), :class:`Matrix` (from *RMatrix*), :class:`Environment` (from *REnvironment*), :class:`Function` (from *RFunction*), :class:`Vector` (from *RVector*). - :class:`robjects.vectors.Vector` lost the (now redundant) methods `subset` and `assign`. Those operations were just aliases to the :class:`ExtractDelegator` :mod:`rpy2.rinterface`: - :data:`globalEnv`, :data:`baseNameSpaceEnv`, and :data:`emptyEnv` were renamed to :data:`globalenv`, :data:`baseenv` and :data:`emptyenv` respectively - The parameter *wantFun* in :meth:`SexpEnvironment.get` is now *wantfun* - The call-back getters and setters are now :func:`get_readconsole`, :func:`set_readconsole`, :func:`get_writeconsole`, :func:`set_writeconsole`, :func:`get_flushconsole`, and :func:`set_flushconsole`. - Functions also accept named parameters equal to Py_None, and transform them to R NULL (previously only accepted parameters inheriting from Sexp). :mod:`rpy2.rlike`: - :class:`ArgDict` becomes :class:`OrdDict`. - :meth:`tags` of :class:`TaggedList` is now a property (with a getter and a setter) :mod:`rpy2.rpy_classic`: - R named lists are returned as Python :class:`dict`, like rpy-1.x does it, with the notable difference that duplicate names are not silently overwritten: an exception of class :class:`ValueError` is thrown whenever happening Bugs fixed ---------- - :meth:`REnvironment.get` now accepts a named parameter *wantFun* (like :meth:`rinterface.SexpEnvironment` does) - :class:`rinterface.SexpVector` will now properly raise an exception when trying to create vector-like object of impossible type - Crash when trying to create a SexpVector of a non-vector type - R objects of class *matrix* are now properly converted into :class:`RMatrix` (instead of :class:`Array`) - :meth:`Robj.as_py` was not working at all (and now it does to some extent) Release 2.0.7 ============= Bugs fixed ---------- - On win32, printing an object was leaving an open file handle behind each time, leading to an error and the impossibility to print (# bug report and fix by Christopher Gutierrez) Release 2.0.6 ============= No user-visible change. Win32-specific additions to the C module were made to compile it. Release 2.0.5 ============= Bugs fixed ---------- - Crash when calling :meth:`SexpEnvironment.get` with an empty string #bug report by Walter Moreira - :meth:`SexpEnvironment.__getitem__` called with an empty string caused unpredictable (and bad) things Release 2.0.4 ============= Bugs fixed ---------- - Added missing named parameter *wantfun* to method :meth:`REnvironment.get` (making it similar to :meth:`SexpEnvironment.get`) - Leak in reference counting when creating SexpVector objects fixed (the symptom was a process growing in size when creating R vector from Python list or numpy arrays) - `R CMD config LAPACK_LIBS` could return an empty string when R was compiled with the veclib framework, causing the setup.py script to raise an exception. setup.py now only print a message about an empty string returned from R CMD config - Numpy arrays with complex elements are no longer causing segfaults - Calls to :meth:`SexpClosure.rcall` with something else that the expected kind of tuple could cause a segfault Release 2.0.3 ============= New Features ------------ :mod:`rpy2.rinterface`: - :meth:`process_revents`, a Wrapper for R_ProcessEvents (# suggested by June Kim to help with issues related to interactive display on win32), and for R_RunHandlers on UNIX-like systems (# patch by Nathaniel Smith). - All callbacks are getting a get to complement the set. (# Patch by Nathaniel Smith) - :meth:`Sexp.__deepcopy__` to copy an object (calling Rf_Duplicate) (# from a patch by Nathaniel Smith) Changes ------- - the default for reading and writing the console are now using sys.stdin and sys.stdout (# patch submitted by Nathaniel Smith) - console IO callbacks (reading and writing) are complemented by one to flush the console - :meth:`Sexp.do_slot_assign` now creates the slot if missing (design-fix - # patch by Nathaniel Smith) Bugs fixed ---------- - fixed problem of numpy interface with R boolean vectors. They are now presented as 'i' rather than 'b' to numpy (# patch submitted by Nathaniel Smith) - The mechanism for setting arbitrary callaback functions for console I/O now ensures that a traceback is printed to stderr whenever an error occurs during the evalutation of the callback (the raised exception used to be silently propagated to the next python call, leading to problems). Release 2.0.2 ============= Bugs fixed ---------- - Fix installation bug when the include directories contain either '-' or 'I' #spotted by James Yoo - Failing to initialize R now throws a RuntimeError - Copying an R "NA" into Python returns a None (and no longer a True) (#fixes a bug reported by Jeff Gentry) Release 2.0.1 ============= New features ------------ :mod:`rpy2.robjects`: - Property `names` for the :class:`RVector` methods :meth:`getnames` and :meth:`setnames` (this was likely forgotten for Release 2.0.0). - Property `rclass` for :class:`RObjectMixin` Changes ------- :mod:`rpy2.robjects`: - :meth:`rclass` becomes :meth:`getrclass` Bugs fixed ---------- - Having the environment variable R_HOME specified resulted in an error when importing :mod:`rpy2.rinterface` # root of the problem spotted by Peter - Setup.py has no longer a (possibly outdated) static hardcoded version number for rpy2 - Testing no longer stops with an error in the absence of the third-party module :mod:`numpy` - :meth:`rpy2.rlike.container.TaggedList.pop` is now returning the element matching the given index Release 2.0.0 ============= New features ------------ - New module :mod:`rpy2.robjects.conversion`. - New module :mod:`rpy2.robjects.numpy2ri` to convert :mod:`numpy` objects into :mod:`rpy2` objects. # adapted from a patch contributed by Nathaniel Smith Changes ------- - :meth:`RObject.__repr__` moved to :meth:`RObject.r_repr` Bugs fixed ---------- - Informative message returned as RuntimeError when failing to find R's HOME - Use the registry to find the R's HOME on win32 # snatched from Peter's earlier contribution to rpy-1.x Release 2.0.0rc1 ================ :mod:`rpy2.rpy_classic`: - :meth:`rpy_classic.RObj.getSexp` moved to a property :attr:`rpy_classic.Robj.sexp`. :mod:`rpy2.robjects`: - :meth:`RObject.__repr__` moved to :meth:`RObject.r_repr` - :meth:`ri2py`, :meth:`ro2py`, and :meth:`py2ri` moved to the new module :mod:`conversion`. Adding the prefix `conversion.` to calls to those functions will be enough to update existing code Bugs fixed ---------- - Informative message returned as RuntimeError when failing to find R's HOME - Use the registry to find the R's HOME on win32 # snatched from Peter's earlier contribution to rpy-1.x Release 2.0.0rc1 ================ New features ------------ - added :data:`__version__` to rpy2/__init__.py :mod:`rpy2.robjects`: - added classes :class:`StrVector`, :class:`IntVector`, :class:`FloatVector`, :class:`BoolVector` :mod:`rpy2.rinterface`: - added missing class :class:`BoolSexpVector`. Changes ------- :mod:`rpy2.robjects`: - does not alias :class:`rinterface.StrSexpVector`, :class:`rinterface.IntSexpVector`, :class:`rinterface.FloatSexpVector` anymore - Constructor for :class:`rpy2.robjects.RDataFrame` checks that R lists are data.frames (not all lists are data.frame) - Formerly new attribute :attr:`_dotter` for :class:`R` is now gone. The documentaion now points to :mod:`rpy2.rpy_classic` for this sort of things. Bugs fixed ---------- - conditional typedef in rinterface.c to compile under win32 # reported and initial proposed fix from Paul Harrington - __pow__ was missing from the delegator object for robjects.RVector (while the documentation was claiming it was there) # bug report by Robert Nuske - Earlier change from Sexp.typeof() to getter Sexp.typeof was not reflected in :mod:`rpy2.rpy_classic` # bug report by Robert Denham Release 2.0.0b1 =============== New features ------------ :mod:`rpy2.robjects`: - added :meth:`setenvironment` for :class:`RFormula`, and defined `environment` as a property - defined `names` as a property for :class:`RVector` :mod:`rpy2.rinterface`: - added functions :func:`get_initoptions` and :func:`set_initoptions`. - new attribute :attr:`_dotter` for :class:`R` singleton. Setting it to True will translate '_' into '.' if the attribute is not found Changes ------- :mod:`rpy2.robjects`: - constructor for RDataFrame now now accepts either :class:`rlike.container.TaggedList` or :class:`rinterface.SexpVector` :mod:`rpy2.rinterface`: - :func:`sexpTypeEmbeddedR` is now called :func:`str_typeint`. - :attr:`initOptions` is now called :attr:`initoptions`. Changes of options can only be done through :func:`set_initoptions`. Bugs fixed ---------- - crash of :meth:`Sexp.enclos` when R not yet initialized (bug report #2078176) - potential crash of :meth:`Sexp.frame` when R not yet initialized - proper reference counting when handling, and deleting, :attr:`Sexp.__sexp__` generated CObjects - setup.py: get properly the include directories (no matter where they are) #bug report and fix adapted from Robert Nuske - setup.py: link to external lapack or blas library when relevant - added a MANIFEST.in ensuring that headers get included in the source distribution #missing headers reported by Nicholas Lewin-Koh - :func:`rinterface.str_typeint` was causing segfault when called with 99 - fixed subsetting for LANGSXP objects Release 2.0.0a3 =============== New features ------------ :mod:`rpy2.rinterface`: - :func:`setReadConsole`: specify Python callback for console input - `R` string vectors can now be built from Python unicode objects - getter :attr:`__sexp__` to return an opaque C pointer to the underlying R object - method :meth:`rsame` to test if the underlying R objects for two :class:`Sexp` are the same. - added `emptyEnv` (R's C-level `R_EmptyEnv`) - added method :meth:`Sexp.do_slot_assign` :mod:`rpy2.robjects`: - R string vectors can now be built from Python unicode objects :mod:`rpy2.rlike`: - module :mod:`functional` with the functions :func:`tapply`, :func:`listify`, :func:`iterify`. - module :mod:`indexing` with the function :func:`order` - method :meth:`TaggedList.sort` now implemented Changes ------- :mod:`rpy2.rinterface`: - :func:`initEmbeddedR` is only initializing if R is not started (no effect otherwise, and no exception thrown anymore) - the method :meth:`Sexp.typeof` was replaced by a Python `getter` :attr:`typeof`. - the method :meth:`Sexp.named` was replaced by a Python `getter` :attr:`named`. - R objects of type LANGSXP are now one kind of vector (... but this may change again) - R objects of type EXPRSXP are now handled as vectors (... but this may change again) - :func:`initEmbeddedR` renamed to :func:`initr` - :func:`endEmbeddedR` renamed to :func:`endr` :mod:`rpy2.robjects`: - :class:`R` remains a singleton, but does not throw an exception when multiple instances are requested Bugs fixed ---------- - unable to compile on Python2.4 (definition of aliases to Python2.5-specific were not where they should be). - overflow issues on Python 2.4/64 bits when indexing R vector with very large integers. - handling of negative indexes for :class:`SexpVector`'s :meth:`__getitem__` and :meth:`__setitem__` was missing - trying to create an instance of :class:`SexpVector` before initializing R raises a RuntimeException (used to segfault) - experimental method :meth:`enclos` was not properly exported - setup.py was exiting prematurely when R was compiled against an existing BLAS library - complex vectors should now be handled properly by :mod:`rpy2.rinterface.robjects`. - methods :meth:`rownames` and :meth:`colnames` for :class:`RDataFrame` were incorrect. Release 2.0.0a2 =============== New features ------------ :mod:`rpy2.rlike`: - package for R-like features in Python - module :mod:`rpy2.rlike.container` - class :class:`ArgsDict` in :mod:`rpy2.rlike.container` - class :class:`TaggedList` in :mod:`rpy2.rlike.container` :mod:`rpy2.rinterface`: - method :meth:`named`, corresponding to R's C-level NAMED - experimental methods :meth:`frame` and :meth:`enclos` for SexpEnvironment corresponding to R's C-level FRAME and ENCLOS - method :meth:`rcall` for :class:`ClosureSexp` - new experimental class :class:`SexpLang` for R language objects. Bugs fixed ---------- - R stack checking is disabled (no longer crashes when multithreading) - fixed missing R_PreserveObject for vectors (causing R part of the object to sometimes vanish during garbage collection) - prevents calling an R function when R has been ended (raise :class:`RuntimeException`). Release 2.0.0a1 =============== New features ------------ :mod:`rpy2.robjects`: - method :meth:`getnames` for :class:`RVector` - experimental methods :meth:`__setitem__` and :meth:`setnames` for :class:`RVector` - method 'getnames' for :class:`RArray` - new class :class:`RFormula` - new helper class :class:`RVectorDelegator` (see below) - indexing RVector the "R way" with subset is now possible through a delegating attribute (e.g., myvec.r[True] rather than myvec.subset(True)). #suggested by Michael Sorich - new class :class:`RDataFrame`. The constructor :meth:`__init__` is still experimental (need for an ordered dictionnary, that will be in before the beta - filled documentation about mapping between objects Changes ------- - many fixes and additions to the documentation - improved GTK console in the demos - changed the major version number to 2 in order to avoid confusion with rpy 1.x # Suggested by Peter and Gregory Warnes - moved test.py to demos/example01.py :mod:`rpy2.robjects`: - changed method name `getNames` to `getnames` where available (all lower-case names for methods seems to be the accepted norm in Python). Bugs fixed ---------- :mod:`rpy2.robjects`: - fixed string representation of R object on Microsoft Windows (using fifo, not available on win32) - :meth:`__getattr__` for :class:`RS4` is now using :meth:`ri2py` :mod:`rpy2.rinterface`: - fixed context of evaluation for R functions (now R_GlobalEnv) Release 1.0a0 ============= - first public release