Source code for rpy2.robjects.environments

import contextlib
import os
import typing
import rpy2.rinterface as rinterface
import rpy2.rinterface_lib.sexp as sexp
from rpy2.robjects.robject import RObjectMixin
from rpy2.robjects import conversion

_new_env = rinterface.baseenv["new.env"]


[docs]class Environment(RObjectMixin, sexp.SexpEnvironment): """ An R environement, implementing Python's mapping interface. """ def __init__(self, o: typing.Optional[sexp.SexpEnvironment] = None): if o is None: o = _new_env(hash=rinterface.BoolSexpVector([True, ])) super(sexp.SexpEnvironment, self).__init__(o) def __getitem__(self, item: str): res = super(Environment, self).__getitem__(item) res = conversion.get_conversion().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: str, value: typing.Any) -> None: robj = conversion.get_conversion().py2rpy(value) super(Environment, self).__setitem__(item, robj) @property def enclos(self) -> typing.Union[sexp.SexpEnvironment, sexp.NULLType]: return conversion.get_conversion().rpy2py(super().enclos) @enclos.setter def enclos(self, value: sexp.SexpEnvironment) -> None: # TODO: I can't figure out why mypy is throwing an error # here. There scope of this assignment is rather limited # (the setter in the parent class SexpEnvironment has the same # signature). super().enclos = value # type: ignore @property def frame(self) -> sexp.SexpEnvironment: return conversion.get_conversion().rpy2py(super().frame)
[docs] def find(self, item: str, wantfun: bool = 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.get_conversion().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
[docs] def keys(self) -> typing.Generator[str, None, None]: """ Return an iterator over keys in the environment.""" return super().keys()
[docs] def items(self) -> typing.Generator[ typing.Tuple[str, sexp.Sexp], None, None]: """ Iterate through the symbols and associated objects in this R environment.""" for k, v in zip(self.keys(), self.values()): yield (k, v)
[docs] def values(self) -> typing.Generator[sexp.Sexp, None, None]: """ Iterate through the objects in this R environment.""" for k in self: yield self[k]
[docs] def pop(self, k: str, *args) -> sexp.Sexp: """ 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
[docs] def popitem(self) -> typing.Tuple[str, sexp.Sexp]: """ 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
[docs] 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]
def __repr__(self): return os.linesep.join( (super(Environment, self).__repr__(), 'n items: {:d}'.format(len(self))) )
@contextlib.contextmanager def local_context( env: typing.Optional[sexp.SexpEnvironment] = None, use_rlock: bool = True ) -> typing.Iterator[Environment]: """Local context for the evaluation of R code. This is a wrapper around the rpy2.rinterface function with the same name. Args: - env: an environment to use as a context. If not specified (None, the default), a child environment to the current context is created. - use_rlock: whether to use a threading lock (see the documentation about "rlock". The default is True. Returns: Yield the environment (passed to env, or created). """ with rinterface.local_context(env=env, use_rlock=use_rlock) as lc: yield Environment(lc)