Revision: 407 http://rpy.svn.sourceforge.net/rpy/?rev=407&view=rev Author: lgautier Date: 2008-03-01 14:45:04 -0800 (Sat, 01 Mar 2008)
Log Message: ----------- Added module robjects Working subset operator for Environments Sexp Too many fixes and additions Modified Paths: -------------- trunk/sandbox/rpy_nextgen/rinterface/rinterface.c trunk/sandbox/rpy_nextgen/setup.py Added Paths: ----------- trunk/sandbox/rpy_nextgen/robjects/ trunk/sandbox/rpy_nextgen/robjects/__init__.py Modified: trunk/sandbox/rpy_nextgen/rinterface/rinterface.c =================================================================== --- trunk/sandbox/rpy_nextgen/rinterface/rinterface.c 2008-02-24 19:07:58 UTC (rev 406) +++ trunk/sandbox/rpy_nextgen/rinterface/rinterface.c 2008-03-01 22:45:04 UTC (rev 407) @@ -6,7 +6,7 @@ * of RPy. * * The authors for the original RPy code, as well as - * belopolsky's contributed code, are listed here as authors; + * belopolsky for his contributed code, are listed here as authors; * parts of this code is (sometimes shamelessly but with great * respect for the work) "inspired" from their contributions. * @@ -29,6 +29,13 @@ * for the specific language governing rights and limitations under the * License. * + * original RPy Authors: Walter Moreira. + * Gregory R. Warnes <[EMAIL PROTECTED]> (Maintainer) + * Original code from wrapping R's C-level SEXPs: belopolsky + * + * This code: Laurent Gautier + * + * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), @@ -86,18 +93,18 @@ Check the documentation for the module this is bundled into if\ you only wish to have an off-the-shelf interface with R.\ \n\ - Example of usage:\ -import rinterface\ -rinterface.initEmbeddedR(\"foo\", \"--verbose\")\ -n = rinterface.SexpVector((100, ), rinterface.REALSXP)\ -hist = rinterface.findVarEmbeddedR(\"hist\")\ -rnorm = rinterface.findVarEmbeddedR(\"rnorm\")\ -x = rnorm(n)\ -hist(x)\ + Example of usage:n\ +import rinterface\n\ +rinterface.initEmbeddedR(\"foo\", \"--verbose\")\n\ +n = rinterface.SexpVector((100, ), rinterface.REALSXP)\n\ +hist = rinterface.globalEnv.get(\"hist\")\n\ +rnorm = rinterface.globalEnv.get(\"rnorm\")\n\ +x = rnorm(n)\n\ +hist(x)\n\ \ -len(x)\ +len(x)\n\ \n\ -$Id$"); +"); //FIXME: check example above @@ -110,6 +117,7 @@ static SexpObject* globalEnv; +static SexpObject* baseNameSpaceEnv; /* --- Initialize and terminate an embedded R --- */ /* Should having multiple threads of R become possible, @@ -117,6 +125,7 @@ */ static PyObject* EmbeddedR_init(PyObject *self, PyObject *args) { + //FIXME: arbitrary number of options //char *defaultargv[] = {"rpython", "--verbose"}; char *options[5] = {"", "", "", "", ""}; @@ -154,7 +163,7 @@ //ending R will not be possible until all such objects are already //deallocated in Python ? //other possibility would be to have a fallback for "unreachable" objects ? - //FIXME: rpy has something to terminate R. Check the details of what it is. + //FIXME: rpy has something to terminate R. Check the details of what they are. if (! PyInt_Check(arg)) { } else { /* sanity checks needed ? */ @@ -217,6 +226,21 @@ * Access to R objects through Python objects */ +/* static PyObject* */ +/* Sexp_new(PyTypeObject *type, PyObject *args) */ +/* { */ +/* PyObject object, res; */ +/* if (!PyArg_ParseTuple(args, "O:new", */ +/* &object)) */ +/* PyErr_Format(PyExc_ValueError, "Can only instanciate from SexpObject"); */ +/* return NULL; */ +/* res = (SexpObject *)_PyObject_New(&Sexp_Type); */ +/* if (!res) */ +/* PyErr_NoMemory(); */ +/* res->sexp = sexp; */ +/* return res; */ +/* } */ + static void Sexp_dealloc(SexpObject *self) { @@ -299,7 +323,7 @@ 0, /*tp_dictoffset*/ 0, /*tp_init*/ 0, /*tp_alloc*/ - 0,//Sexp_new, /*tp_new*/ + 0, //Sexp_new, /*tp_new*/ 0, /*tp_free*/ 0, /*tp_is_gc*/ }; @@ -387,24 +411,49 @@ PyObject *tmp_obj; int is_SexpObject; for (arg_i=0; arg_i<largs; arg_i++) { - //FIXME: assert that all are SexpObjects tmp_obj = PyTuple_GetItem(args, arg_i); is_SexpObject = PyObject_TypeCheck(tmp_obj, &Sexp_Type); if (! is_SexpObject) { PyErr_Format(PyExc_ValueError, "All parameters must be of type Sexp_Type."); - return NULL; + goto fail; } tmp_R = ((SexpObject *)tmp_obj)->sexp; SETCAR(c_R, tmp_R); c_R = CDR(c_R); } + + /* named args */ + PyObject *citems, *argValue, *argName; + char *argNameString; + + if (kwds) { + citems = PyMapping_Items(kwds); + } + for (arg_i=0; arg_i<lkwds; arg_i++) { + tmp_obj = PySequence_GetItem(citems, arg_i); + if (! tmp_obj) + goto fail; + argName = PyTuple_GetItem(tmp_obj, 0); + if (! PyString_Check(argName)) { + PyErr_SetString(PyExc_TypeError, "keywords must be strings"); + goto fail; + } + argValue = PyTuple_GetItem(tmp_obj, 1); + is_SexpObject = PyObject_TypeCheck(argValue, &Sexp_Type); + if (! is_SexpObject) { + PyErr_Format(PyExc_ValueError, "All parameters must be of type Sexp_Type."); + goto fail; + } + Py_DECREF(tmp_obj); + tmp_R = ((SexpObject *)argValue)->sexp; + SETCAR(c_R, tmp_R); + argNameString = PyString_AsString(argName); + SET_TAG(c_R, install(argNameString)); + //printf("PyMem_Free..."); + //PyMem_Free(argNameString); + } + Py_XDECREF(citems); - //FIXME: implement named parameters. -/* if (!make_kwds(lkwds, kwds, &e)) { */ -/* UNPROTECT(1); */ -/* return NULL; */ -/* } */ - //FIXME: R_GlobalContext ? PROTECT(res_R = do_eval_expr(call_R)); @@ -416,9 +465,17 @@ //FIXME: standardize R outputs extern void Rf_PrintWarnings(void); Rf_PrintWarnings(); /* show any warning messages */ - + PyObject *res = (PyObject *)newSexpObject(res_R); return res; + + fail: + printf("failed.\n"); + Py_XDECREF(citems); + Py_DECREF(tmp_obj); + UNPROTECT(1); + return NULL; + } @@ -506,9 +563,6 @@ if (!PyArg_ParseTuple(args, "Oi:new", &seq, &rType)) return NULL; - #ifdef VERBOSE - printf("type: %i\n", rType); - #endif const SEXP sexp = newSEXP(seq, rType); PyObject *res = (PyObject *)newSexpObject(sexp); return res; @@ -670,8 +724,8 @@ const SEXP rho_R = ((SexpObject *)self)->sexp; - if (rho_R == NULL) { - PyErr_Format(PyExc_LookupError, "Fatal error: NULL environment."); + if (rho_R == R_EmptyEnv) { + PyErr_Format(PyExc_LookupError, "Fatal error: R_UnboundValue."); } res_R = findVar(install(name), rho_R); @@ -708,9 +762,8 @@ name = PyString_AsString(key); SEXP rho_R = ((SexpObject *)self)->sexp; - res_R = findVarInFrame(install(name), rho_R); + res_R = findVarInFrame(rho_R, install(name)); - if (res_R != R_UnboundValue) { return newSexpObject(res_R); } @@ -733,14 +786,17 @@ // be made like mappings at the Python level ? PyDoc_STRVAR(EnvironmentSexp_Type_doc, "R object that is an environment.\ - R environments can be seen as similar to Python\n\ - dictionnaries, with the twist that looking for\ - a key can be recursively propagated to the enclosing\n\ - environment whenever the key is not found.\ + R environments can be seen as similar to Python\ + dictionnaries, with the following twists:\n\ + - an environment can be a list of frames to sequentially\ + search into\n\ +- the search can be recursively propagated to the enclosing\ + environment whenever the key is not found (in that respect\ + they can be seen as scopings).\n\ \n\ - The subsetting operator is made to match Python's\n\ + The subsetting operator \"[\" is made to match Python's\ behavior, that is the enclosing environments are not\ - inspected upon absence of a given key.\ + inspected upon absence of a given key.\n\ "); static PyTypeObject EnvironmentSexp_Type = { @@ -976,6 +1032,18 @@ } } break; + case LISTSXP: + for (i = 0; i < length; ++i) { + if(item = PySequence_Fast_GET_ITEM(seq_object, i)) { + int is_SexpObject = PyObject_TypeCheck(item, &Sexp_Type); + if (! is_SexpObject) { + PyErr_Format(PyExc_ValueError, "All elements of the list must be of " + "type 'Sexp_Type'."); + return NULL; + } + SET_ELEMENT(sexp, i, ((SexpObject *)item)->sexp); + } + } //FIXME: add complex default: PyErr_Format(PyExc_ValueError, "cannot handle type %d", rType); @@ -1092,13 +1160,20 @@ Py_INCREF(ErrorObject); PyModule_AddObject(m, "RobjectNotFound", ErrorObject); - globalEnv = (SexpObject*)_PyObject_New(&EnvironmentSexp_Type); - globalEnv->sexp = NULL; - if (PyDict_SetItemString(d, "globalEnv", (PyObject*)globalEnv) < 0) + globalEnv = (SexpObject *)_PyObject_New(&EnvironmentSexp_Type); + globalEnv->sexp = R_EmptyEnv; + if (PyDict_SetItemString(d, "globalEnv", (PyObject *)globalEnv) < 0) return; //FIXME: DECREF ? - //Py_DECREF(globalEnv); + Py_DECREF(globalEnv); + baseNameSpaceEnv = (SexpObject*)_PyObject_New(&EnvironmentSexp_Type); + globalEnv->sexp = R_EmptyEnv; + if (PyDict_SetItemString(d, "baseNameSpaceEnv", (PyObject *)baseNameSpaceEnv) < 0) + return; + //FIXME: DECREF ? + Py_DECREF(baseNameSpaceEnv); + /* Add some symbolic constants to the module */ ADD_INT_CONSTANT(m, NILSXP); ADD_INT_CONSTANT(m, SYMSXP); @@ -1124,4 +1199,27 @@ ADD_INT_CONSTANT(m, RAWSXP); ADD_INT_CONSTANT(m, S4SXP); + /* "Logical" (boolean) values */ + ADD_INT_CONSTANT(m, TRUE); + ADD_INT_CONSTANT(m, FALSE); + + /* R_ext/Arith.h */ + ADD_INT_CONSTANT(m, NA_LOGICAL); + ADD_INT_CONSTANT(m, NA_INTEGER); + PyObject *na_real = PyFloat_FromDouble(NA_REAL); + if (PyDict_SetItemString(d, "NA_REAL", (PyObject *)na_real) < 0) + return; + //FIXME: DECREF ? + Py_DECREF(na_real); + + + /* Rinternals.h */ + SexpObject *na_string = (SexpObject *)_PyObject_New(&VectorSexp_Type); + na_string->sexp = NA_STRING; + if (PyDict_SetItemString(d, "NA_STRING", (PyObject *)na_string) < 0) + return; + //FIXME: DECREF ? + Py_DECREF(na_string); + + } Added: trunk/sandbox/rpy_nextgen/robjects/__init__.py =================================================================== --- trunk/sandbox/rpy_nextgen/robjects/__init__.py (rev 0) +++ trunk/sandbox/rpy_nextgen/robjects/__init__.py 2008-03-01 22:45:04 UTC (rev 407) @@ -0,0 +1,150 @@ +import array +import rinterface + + +#FIXME: close everything when leaving (check RPy for that). + + + +def defaultPy2RMapper(self, o): + if isinstance(o, Robject): + return o._sexp + if isinstance(o, array.array): + if o.typecode in ('h', 'H', 'i', 'I'): + res = rinterface.SexpVector(o, rinterface.INTSXP) + elif o.typecode in ('f', 'd'): + res = rinterface.SexpVector(o, rinterface.REALSXP) + else: + raise("Nothing can be done for this array type at the moment.") + elif isinstance(o, int): + res = rinterface.SexpVector([o, ], rinterface.INTSXP) + elif isinstance(o, float): + res = rinterface.SexpVector([o, ], rinterface.REALSXP) + elif isinstance(o, bool): + res = rinterface.SexpVector([o, ], rinterface.LGLSXP) + elif isinstance(o, str): + res = rinterface.SexpVector([o, ], rinterface.STRSXP) + else: + raise("Nothing can be done for this type at the moment.") + return res + +def defaultR2PyMapper(o): + if isinstance(o, rinterface.SexpVector): + res = Rvector(o) + elif isinstance(o, rinterface.SexpClosure): + res = Rfunction(o) + else: + res = o + return res + + +#FIXME: clean and nice mechanism to allow user-specifier mapping function +mapper = defaultR2PyMapper + + + + +#FIXME: Looks hackish. inheritance should be used ? +class Robject(object): + pass + +class Rvector(Robject): + + def __init__(self, o): + if (isinstance(o, rinterface.SexpVector)): + self._sexp = o + else: + self._sexp = defaultPy2RMapper(self, o) + + def subset(self, *args): + for a in args: + if not isinstance(a, rinterface.SexpVector): + raise(TypeError("Subset only take R vectors")) + res = r.globalEnv.get("[")([self._sexp, ] + args)#, drop=drop) + return res + + def __getitem__(self, i): + res = self._sexp[i] + return res + + def __add__(self, x): + res = r.globalEnv.get("+")(self._sexp, x) + return res + + def __sub__(self, x): + res = r.globalEnv.get("-")(self._sexp, x) + return res + + def __mul__(self, x): + res = r.globalEnv.get("*")(self._sexp, x) + return res + + def __div__(self, x): + res = r.globalEnv.get("/")(self._sexp, x) + return res + + def __divmod__(self, x): + res = r.globalEnv.get("%%")(self._sexp, x) + return res + + def __or__(self, x): + res = r.globalEnv.get("|")(self._sexp, x) + return res + + def __and__(self, x): + res = r.globalEnv.get("&")(self._sexp, x) + return res + + +class Rfunction(Robject): + + mapper = defaultPy2RMapper + + def __init__(self, o): + if (isinstance(o, rinterface.SexpClosure)): + self._sexp = o + else: + raise("Cannot instantiate.") + + def __call__(self, *args, **kwargs): + new_args = [self.mapper(a) for a in args] + new_kwargs = {} + for k, v in kwargs.iteritems(): + new_kwargs[k] = self.mapper(v) + res = self._sexp(*new_args, **new_kwargs) + res = mapper(res) + return res + + +class Renvironment(Robject): + def __init__(self, o): + if (isinstance(o, rinterface.SexpEnvironment)): + self._sexp = o + else: + raise("Cannot instantiate") + + def __getattr__(self, attr): + res = self._sexp.get(attr) + return res + + + +class R(object): + _instance = None + + def __init__(self, options): + if R._instance is None: + args = ["robjects", ] + options + rinterface.initEmbeddedR(*args) + R._instance = self + else: + raise("Only one instance of R can be created") + + def __getattribute__(self, attr): + res = rinterface.globalEnv.get(attr) + res = mapper(res) + return res + +r = R(["--no-save", ]) + + Modified: trunk/sandbox/rpy_nextgen/setup.py =================================================================== --- trunk/sandbox/rpy_nextgen/setup.py 2008-02-24 19:07:58 UTC (rev 406) +++ trunk/sandbox/rpy_nextgen/setup.py 2008-03-01 22:45:04 UTC (rev 407) @@ -9,7 +9,7 @@ print('R\'s home is:%s' %RHOME) -r_libs = [os.path.join(RHOME, 'lib')] +r_libs = [os.path.join(RHOME, 'lib'), os.path.join(RHOME, 'modules')] rinterface = Extension( @@ -26,7 +26,7 @@ version="0.0.1", description="Python interface to the R language", url="http://rpy.sourceforge.net", - license="GPL", + license="(L)GPL", ext_modules=[rinterface], - #py_modules=['rpython'] + py_modules=['robjects'] ) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ rpy-list mailing list rpy-list@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/rpy-list