Author: Wim Lavrijsen <[email protected]>
Branch: reflex-support
Changeset: r56206:5bb835b9bca6
Date: 2012-07-17 14:37 -0700
http://bitbucket.org/pypy/pypy/changeset/5bb835b9bca6/
Log: first attempt at pythonizations in rpython
diff --git a/pypy/module/cppyy/__init__.py b/pypy/module/cppyy/__init__.py
--- a/pypy/module/cppyy/__init__.py
+++ b/pypy/module/cppyy/__init__.py
@@ -22,3 +22,12 @@
'load_reflection_info' : 'pythonify.load_reflection_info',
'add_pythonization' : 'pythonify.add_pythonization',
}
+
+ def __init__(self, space, *args):
+ "NOT_RPYTHON"
+ MixedModule.__init__(self, space, *args)
+
+ # pythonization functions may be written in RPython, but the interp2app
+ # code generation is not, so give it a chance to run now
+ from pypy.module.cppyy import capi
+ capi.register_pythonizations(space)
diff --git a/pypy/module/cppyy/capi/__init__.py
b/pypy/module/cppyy/capi/__init__.py
--- a/pypy/module/cppyy/capi/__init__.py
+++ b/pypy/module/cppyy/capi/__init__.py
@@ -6,6 +6,8 @@
identify = backend.identify
pythonize = backend.pythonize
+register_pythonizations = backend.register_pythonizations
+
ts_reflect = backend.ts_reflect
ts_call = backend.ts_call
ts_memory = backend.ts_memory
diff --git a/pypy/module/cppyy/capi/cint_capi.py
b/pypy/module/cppyy/capi/cint_capi.py
--- a/pypy/module/cppyy/capi/cint_capi.py
+++ b/pypy/module/cppyy/capi/cint_capi.py
@@ -1,9 +1,16 @@
-import py, os
+import py, os, sys
+
+from pypy.interpreter.error import OperationError
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.typedef import TypeDef
from pypy.translator.tool.cbuild import ExternalCompilationInfo
from pypy.rpython.lltypesystem import rffi
from pypy.rlib import libffi, rdynload
+from pypy.module.itertools import interp_itertools
+
+
__all__ = ['identify', 'eci', 'c_load_dictionary']
pkgpath = py.path.local(__file__).dirpath().join(os.pardir)
@@ -39,12 +46,15 @@
_cintdll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL |
rdynload.RTLD_NOW)
with rffi.scoped_str2charp('libCore.so') as ll_libname:
_coredll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL |
rdynload.RTLD_NOW)
+#with rffi.scoped_str2charp('libTree.so') as ll_libname:
+# _coredll = rdynload.dlopen(ll_libname, rdynload.RTLD_GLOBAL |
rdynload.RTLD_NOW)
eci = ExternalCompilationInfo(
separate_module_files=[srcpath.join("cintcwrapper.cxx")],
include_dirs=[incpath] + rootincpath,
includes=["cintcwrapper.h"],
library_dirs=rootlibpath,
+# link_extra=["-lCore", "-lCint", "-lTree"],
link_extra=["-lCore", "-lCint"],
use_cpp_linker=True,
)
@@ -62,9 +72,148 @@
raise rdynload.DLOpenError(err)
return libffi.CDLL(name) # should return handle to already open file
-# CINT-specific pythonizations
+
+# CINT-specific pythonizations ===============================================
+
+### TTree --------------------------------------------------------------------
+_ttree_Branch = rffi.llexternal(
+ "cppyy_ttree_Branch",
+ [rffi.VOIDP, rffi.CCHARP, rffi.CCHARP, rffi.VOIDP, rffi.INT, rffi.INT],
rffi.LONG,
+ threadsafe=False,
+ compilation_info=eci)
+
+@unwrap_spec(args_w='args_w')
+def ttree_Branch(space, w_self, args_w):
+ """Pythonized version of TTree::Branch(): takes proxy objects and by-passes
+ the CINT-manual layer."""
+
+ from pypy.module.cppyy import interp_cppyy
+ tree_class = interp_cppyy.scope_byname(space, "TTree")
+
+ # sigs to modify (and by-pass CINT):
+ # 1. (const char*, const char*, T**, Int_t=32000, Int_t=99)
+ # 2. (const char*, T**, Int_t=32000, Int_t=99)
+ argc = len(args_w)
+
+ # basic error handling of wrong arguments is best left to the original
call,
+ # so that error messages etc. remain consistent in appearance: the
following
+ # block may raise TypeError or IndexError to break out anytime
+
+ try:
+ if argc < 2 or 5 < argc:
+ raise TypeError("wrong number of arguments")
+
+ tree = space.interp_w(interp_cppyy.W_CPPInstance, w_self,
can_be_None=True)
+ if (tree is None) or (tree.cppclass != tree_class):
+ raise TypeError("not a TTree")
+
+ # first argument must always always be cont char*
+ branchname = space.str_w(args_w[0])
+
+ # if args_w[1] is a classname, then case 1, else case 2
+ try:
+ classname = space.str_w(args_w[1])
+ addr_idx = 2
+ w_address = args_w[addr_idx]
+ except OperationError:
+ addr_idx = 1
+ w_address = args_w[addr_idx]
+
+ bufsize, splitlevel = 32000, 99
+ if addr_idx+1 < argc: bufsize = space.c_int_w(args_w[addr_idx+1])
+ if addr_idx+2 < argc: splitlevel = space.c_int_w(args_w[addr_idx+2])
+
+ # now retrieve the W_CPPInstance and build other stub arguments
+ cppinstance = space.interp_w(interp_cppyy.W_CPPInstance, w_address)
+ address = rffi.cast(rffi.VOIDP, cppinstance.get_rawobject())
+ klassname = cppinstance.cppclass.name
+ vtree = rffi.cast(rffi.VOIDP, tree.get_rawobject())
+
+ # call the helper stub to by-pass CINT
+ vbranch = _ttree_Branch(vtree, branchname, klassname, address,
bufsize, splitlevel)
+ branch_class = interp_cppyy.scope_byname(space, "TBranch")
+ space = tree.space # holds the class cache in State
+ w_branch = interp_cppyy.wrap_cppobject(
+ space, space.w_None, branch_class, vbranch, isref=False,
python_owns=False)
+ return w_branch
+ except (OperationError, TypeError, IndexError), e:
+ pass
+
+ # return control back to the original, unpythonized overload
+ return tree_class.get_overload("Branch").call(w_self, args_w)
+
+class W_TTreeIter(interp_itertools.W_Count):
+ def __init__(self, space, w_firstval, w_step):
+ interp_itertools.W_Count.__init__(self, space, w_firstval, w_step)
+ self.w_tree = space.w_None
+
+ def set_tree(self, space, w_tree):
+ self.w_tree = w_tree
+ try:
+ space.getattr(self.w_tree, space.wrap("_pythonized"))
+ except OperationError:
+ from pypy.module.cppyy import interp_cppyy
+ tree = space.interp_w(interp_cppyy.W_CPPInstance, self.w_tree)
+ self.space = space = tree.space # holds the class cache in
State
+ w_branches = space.call_method(self.w_tree, "GetListOfBranches")
+ for i in range(space.int_w(space.call_method(w_branches,
"GetEntriesFast"))):
+ w_branch = space.call_method(w_branches, "At", space.wrap(i))
+ w_name = space.call_method(w_branch, "GetName")
+ w_klassname = space.call_method(w_branch, "GetClassName")
+ klass = interp_cppyy.scope_byname(space,
space.str_w(w_klassname))
+ w_obj = klass.construct()
+ space.call_method(w_branch, "SetObject", w_obj)
+ # cache the object and define this tree pythonized
+ space.setattr(self.w_tree, w_name, w_obj)
+ space.setattr(self.w_tree, space.wrap("_pythonized"),
space.w_True)
+
+ def iter_w(self):
+ return self.space.wrap(self)
+
+ def next_w(self):
+ w_bytes_read = self.space.call_method(self.w_tree, "GetEntry",
self.w_c)
+ if not self.space.is_true(w_bytes_read):
+ raise OperationError(self.space.w_StopIteration, self.space.w_None)
+ w_c = self.w_c
+ self.w_c = self.space.add(w_c, self.w_step)
+ return w_c
+
+W_TTreeIter.typedef = TypeDef(
+ 'TTreeIter',
+ __iter__ = interp2app(W_TTreeIter.iter_w),
+ next = interp2app(W_TTreeIter.next_w),
+)
+
+@unwrap_spec(args_w='args_w')
+def ttree_iter(space, w_self, args_w):
+ """Allow iteration over TTree's. Also initializes branch data members and
+ sets addresses, if needed."""
+ w_treeiter = W_TTreeIter(space, space.wrap(0), space.wrap(1))
+ w_treeiter.set_tree(space, w_self)
+ return w_treeiter
+
+# setup pythonizations for later use at run-time
+_pythonizations = {}
+def register_pythonizations(space):
+ "NOT_RPYTHON"
+
+ ### TTree
+ _pythonizations['ttree_Branch'] = space.wrap(interp2app(ttree_Branch))
+ _pythonizations['ttree_iter'] = space.wrap(interp2app(ttree_iter))
+
+# callback coming in when app-level bound classes have been created
def pythonize(space, name, w_pycppclass):
- if name[0:8] == "TVectorT":
+ if name == 'TFile':
+ space.setattr(w_pycppclass, space.wrap("__getattr__"),
+ space.getattr(w_pycppclass, space.wrap("Get")))
+
+ elif name == 'TTree':
+ space.setattr(w_pycppclass, space.wrap("_unpythonized_Branch"),
+ space.getattr(w_pycppclass, space.wrap("Branch")))
+ space.setattr(w_pycppclass, space.wrap("Branch"),
_pythonizations["ttree_Branch"])
+ space.setattr(w_pycppclass, space.wrap("__iter__"),
_pythonizations["ttree_iter"])
+
+ elif name[0:8] == "TVectorT": # TVectorT<> template
space.setattr(w_pycppclass, space.wrap("__len__"),
space.getattr(w_pycppclass, space.wrap("GetNoElements")))
diff --git a/pypy/module/cppyy/capi/reflex_capi.py
b/pypy/module/cppyy/capi/reflex_capi.py
--- a/pypy/module/cppyy/capi/reflex_capi.py
+++ b/pypy/module/cppyy/capi/reflex_capi.py
@@ -42,6 +42,11 @@
def c_load_dictionary(name):
return libffi.CDLL(name)
+
# Reflex-specific pythonizations
+def register_pythonizations(space):
+ "NOT_RPYTHON"
+ pass
+
def pythonize(space, name, w_pycppclass):
pass
diff --git a/pypy/module/cppyy/converter.py b/pypy/module/cppyy/converter.py
--- a/pypy/module/cppyy/converter.py
+++ b/pypy/module/cppyy/converter.py
@@ -353,10 +353,9 @@
try:
buf = space.buffer_w(w_obj)
x[0] = rffi.cast(rffi.VOIDP, buf.get_raw_address())
- ba[capi.c_function_arg_typeoffset()] = 'o'
except (OperationError, ValueError):
x[0] = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj))
- ba[capi.c_function_arg_typeoffset()] = 'a'
+ ba[capi.c_function_arg_typeoffset()] = 'o'
def convert_argument_libffi(self, space, w_obj, argchain, call_local):
argchain.arg(get_rawobject(space, w_obj))
diff --git a/pypy/module/cppyy/include/cintcwrapper.h
b/pypy/module/cppyy/include/cintcwrapper.h
--- a/pypy/module/cppyy/include/cintcwrapper.h
+++ b/pypy/module/cppyy/include/cintcwrapper.h
@@ -7,8 +7,14 @@
extern "C" {
#endif // ifdef __cplusplus
+ /* misc helpers */
void* cppyy_load_dictionary(const char* lib_name);
+ /* pythonization helpers */
+ cppyy_object_t cppyy_ttree_Branch(
+ void* vtree, const char* branchname, const char* classname,
+ void* addobj, int bufsize, int splitlevel);
+
#ifdef __cplusplus
}
#endif // ifdef __cplusplus
diff --git a/pypy/module/cppyy/interp_cppyy.py
b/pypy/module/cppyy/interp_cppyy.py
--- a/pypy/module/cppyy/interp_cppyy.py
+++ b/pypy/module/cppyy/interp_cppyy.py
@@ -522,6 +522,9 @@
def __eq__(self, other):
return self.handle == other.handle
+ def __ne__(self, other):
+ return self.handle != other.handle
+
# For now, keep namespaces and classes separate as namespaces are extensible
# with info from multiple dictionaries and do not need to bother with meta
@@ -613,7 +616,12 @@
_immutable_ = True
kind = "class"
+ def __init__(self, space, name, opaque_handle):
+ W_CPPScope.__init__(self, space, name, opaque_handle)
+ self.default_constructor = None
+
def _make_cppfunction(self, pyname, index):
+ default_constructor = False
num_args = capi.c_method_num_args(self, index)
args_required = capi.c_method_req_args(self, index)
arg_defs = []
@@ -623,13 +631,18 @@
arg_defs.append((arg_type, arg_dflt))
if capi.c_is_constructor(self, index):
cls = CPPConstructor
+ if args_required == 0:
+ default_constructor = True
elif capi.c_is_staticmethod(self, index):
cls = CPPFunction
elif pyname == "__setitem__":
cls = CPPSetItem
else:
cls = CPPMethod
- return cls(self.space, self, index, arg_defs, args_required)
+ cppfunction = cls(self.space, self, index, arg_defs, args_required)
+ if default_constructor:
+ self.default_constructor = cppfunction
+ return cppfunction
def _find_datamembers(self):
num_datamembers = capi.c_num_datamembers(self)
@@ -643,6 +656,11 @@
datamember = W_CPPDataMember(self.space, self, type_name, offset,
is_static)
self.datamembers[datamember_name] = datamember
+ def construct(self):
+ if self.default_constructor is not None:
+ return self.default_constructor.call(capi.C_NULL_OBJECT, [])
+ raise self.missing_attribute_error("default_constructor")
+
def find_overload(self, name):
raise self.missing_attribute_error(name)
@@ -843,6 +861,7 @@
return w_pycppclass
def wrap_new_cppobject_nocast(space, w_pycppclass, cppclass, rawobject, isref,
python_owns):
+ rawobject = rffi.cast(capi.C_OBJECT, rawobject)
if space.is_w(w_pycppclass, space.w_None):
w_pycppclass = get_pythonized_cppclass(space, cppclass.handle)
w_cppinstance = space.allocate_instance(W_CPPInstance, w_pycppclass)
@@ -852,12 +871,14 @@
return w_cppinstance
def wrap_cppobject_nocast(space, w_pycppclass, cppclass, rawobject, isref,
python_owns):
+ rawobject = rffi.cast(capi.C_OBJECT, rawobject)
obj = memory_regulator.retrieve(rawobject)
if obj is not None and obj.cppclass is cppclass:
return obj
return wrap_new_cppobject_nocast(space, w_pycppclass, cppclass, rawobject,
isref, python_owns)
def wrap_cppobject(space, w_pycppclass, cppclass, rawobject, isref,
python_owns):
+ rawobject = rffi.cast(capi.C_OBJECT, rawobject)
if rawobject:
actual = capi.c_actual_class(cppclass, rawobject)
if actual != cppclass.handle:
diff --git a/pypy/module/cppyy/src/cintcwrapper.cxx
b/pypy/module/cppyy/src/cintcwrapper.cxx
--- a/pypy/module/cppyy/src/cintcwrapper.cxx
+++ b/pypy/module/cppyy/src/cintcwrapper.cxx
@@ -21,6 +21,10 @@
#include "TMethod.h"
#include "TMethodArg.h"
+// for pythonization
+#include "TTree.h"
+#include "TBranch.h"
+
#include "Api.h"
#include <assert.h>
@@ -903,3 +907,13 @@
return (void*)1;
return (void*)0;
}
+
+
+/* pythonization helpers -------------------------------------------------- */
+cppyy_object_t cppyy_ttree_Branch(void* vtree, const char* branchname, const
char* classname,
+ void* addobj, int bufsize, int splitlevel) {
+ // this little song-and-dance is to by-pass the handwritten Branch methods
+ TBranch* b = ((TTree*)vtree)->Bronch(branchname, classname,
(void*)&addobj, bufsize, splitlevel);
+ b->SetObject(addobj);
+ return (cppyy_object_t)b;
+}
diff --git a/pypy/module/cppyy/test/test_cint.py
b/pypy/module/cppyy/test/test_cint.py
--- a/pypy/module/cppyy/test/test_cint.py
+++ b/pypy/module/cppyy/test/test_cint.py
@@ -108,3 +108,58 @@
assert len(v) == N
for j in v:
assert round(v[int(math.sqrt(j)+0.5)]-j, 5) == 0.
+
+
+class AppTestCINTTTree:
+ def setup_class(cls):
+ cls.space = space
+ cls.w_N = space.wrap(5)
+ cls.w_M = space.wrap(10)
+ cls.w_fname = space.wrap("test.root")
+ cls.w_tname = space.wrap("test")
+ cls.w_title = space.wrap("test tree")
+ cls.space.appexec([], """():
+ import cppyy""")
+
+ def test01_write_stdvector( self ):
+ """Test writing of a single branched TTree with an
std::vector<double>"""
+
+ from cppyy import gbl # bootstraps, only needed for tests
+ from cppyy.gbl import TFile, TTree
+ from cppyy.gbl.std import vector
+
+ f = TFile(self.fname, "RECREATE")
+ t = TTree(self.tname, self.title)
+ t._python_owns = False
+
+ v = vector("double")()
+ raises(TypeError, TTree.Branch, None, "mydata", v.__class__.__name__,
v)
+ raises(TypeError, TTree.Branch, v, "mydata", v.__class__.__name__, v)
+
+ t.Branch("mydata", v.__class__.__name__, v)
+
+ for i in range(self.N):
+ for j in range(self.M):
+ v.push_back(i*self.M+j)
+ t.Fill()
+ v.clear()
+ f.Write()
+ f.Close()
+
+ def test02_read_stdvector(self):
+ """Test reading of a single branched TTree with an
std::vector<double>"""
+
+ from cppyy import gbl # bootstraps, only needed for tests
+ from cppyy.gbl import TFile
+
+ f = TFile(self.fname)
+ mytree = f.Get(self.tname)
+
+ i = 0
+ for event in mytree:
+ for entry in mytree.mydata:
+ assert i == int(entry)
+ i += 1
+ assert i == self.N * self.M
+
+ f.Close()
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit