Author: Wim Lavrijsen <[email protected]>
Branch: reflex-support
Changeset: r56913:1a8daa7fe87d
Date: 2012-08-28 13:38 -0700
http://bitbucket.org/pypy/pypy/changeset/1a8daa7fe87d/
Log: reworked typical pythonizations
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
@@ -72,6 +72,14 @@
# CINT-specific pythonizations ===============================================
+def _get_string_data(space, w_obj, m1, m2 = None):
+ from pypy.module.cppyy import interp_cppyy
+ obj = space.interp_w(interp_cppyy.W_CPPInstance, w_obj)
+ w_1 = obj.space.call_method(w_obj, m1)
+ if m2 is None:
+ return w_1
+ return obj.space.call_method(w_1, m2)
+
### TTree --------------------------------------------------------------------
_ttree_Branch = rffi.llexternal(
"cppyy_ttree_Branch",
@@ -211,25 +219,41 @@
def register_pythonizations(space):
"NOT_RPYTHON"
- ### TTree
- _pythonizations['ttree_Branch'] = space.wrap(interp2app(ttree_Branch))
- _pythonizations['ttree_iter'] = space.wrap(interp2app(ttree_iter))
- _pythonizations['ttree_getattr'] = space.wrap(interp2app(ttree_getattr))
+ allfuncs = [
+
+ ### TTree
+ ttree_Branch, ttree_iter, ttree_getattr,
+ ]
+
+ for f in allfuncs:
+ _pythonizations[f.__name__] = space.wrap(interp2app(f))
+
+def _method_alias(space, w_pycppclass, m1, m2):
+ space.setattr(w_pycppclass, space.wrap(m1),
+ space.getattr(w_pycppclass, space.wrap(m2)))
# callback coming in when app-level bound classes have been created
def pythonize(space, name, w_pycppclass):
- if name == 'TFile':
- space.setattr(w_pycppclass, space.wrap("__getattr__"),
- space.getattr(w_pycppclass, space.wrap("Get")))
+ if name == "TFile":
+ _method_alias(space, w_pycppclass, "__getattr__", "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 == "TObjString":
+ _method_alias(space, w_pycppclass, "__str__", "GetName")
+ _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "GetString")
+
+ elif name == "TString":
+ _method_alias(space, w_pycppclass, "__str__", "Data")
+ _method_alias(space, w_pycppclass, "__len__", "Length")
+ _method_alias(space, w_pycppclass, "__cmp__", "CompareTo")
+ _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "Data")
+
+ elif name == "TTree":
+ _method_alias(space, w_pycppclass, "_unpythonized_Branch", "Branch")
+
+ space.setattr(w_pycppclass, space.wrap("Branch"),
_pythonizations["ttree_Branch"])
+ space.setattr(w_pycppclass, space.wrap("__iter__"),
_pythonizations["ttree_iter"])
space.setattr(w_pycppclass, space.wrap("__getattr__"),
_pythonizations["ttree_getattr"])
elif name[0:8] == "TVectorT": # TVectorT<> template
- space.setattr(w_pycppclass, space.wrap("__len__"),
- space.getattr(w_pycppclass, space.wrap("GetNoElements")))
+ _method_alias(space, w_pycppclass, "__len__", "GetNoElements")
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
@@ -880,22 +880,42 @@
ptrptr = rffi.cast(rffi.VOIDPP, self._rawobject)
return rffi.cast(capi.C_OBJECT, ptrptr[0])
+ def _get_as_builtin(self):
+ try:
+ return self.space.call_method(self.space.wrap(self),
"_cppyy_as_builtin")
+ except OperationError, e:
+ if not e.match(self.space, self.space.w_AttributeError):
+ raise
+ return None
+
def instance__eq__(self, w_other):
- other = self.space.interp_w(W_CPPInstance, w_other, can_be_None=False)
# get here if no class-specific overloaded operator is available, try
to
# find a global overload in gbl, in __gnu_cxx (for iterators), or in
the
- # scopes of the argument classes (TODO: implement that last)
- for name in ["", "__gnu_cxx"]:
- nss = scope_byname(self.space, name)
- meth_idx = capi.c_get_global_operator(nss, self.cppclass,
other.cppclass, "==")
- if meth_idx != -1:
- f = nss._make_cppfunction("operator==", meth_idx)
- ol = W_CPPOverload(self.space, nss, [f])
- # TODO: cache this operator
- return ol.call(self, [self, w_other])
-
- # fallback: direct pointer comparison (the class comparison is needed
since the
- # first data member in a struct and the struct have the same address)
+ # scopes of the argument classes (TODO: implement that last option)
+ try:
+ # TODO: expecting w_other to be an W_CPPInstance is too limiting
+ other = self.space.interp_w(W_CPPInstance, w_other,
can_be_None=False)
+ for name in ["", "__gnu_cxx"]:
+ nss = scope_byname(self.space, name)
+ meth_idx = capi.c_get_global_operator(nss, self.cppclass,
other.cppclass, "==")
+ if meth_idx != -1:
+ f = nss._make_cppfunction("operator==", meth_idx)
+ ol = W_CPPOverload(self.space, nss, [f])
+ # TODO: cache this operator (not done yet, as the above
does not
+ # select all overloads)
+ return ol.call(self, [self, w_other])
+ except OperationError, e:
+ if not e.match(self.space, self.space.w_TypeError):
+ raise
+
+ # fallback 1: convert the object to a builin equivalent
+ w_as_builtin = self._get_as_builtin()
+ if w_as_builtin is not None:
+ return self.space.eq(w_as_builtin, w_other)
+
+ # fallback 2: direct pointer comparison (the class comparison is
needed since
+ # the first data member in a struct and the struct have the same
address)
+ other = self.space.interp_w(W_CPPInstance, w_other, can_be_None=False)
# TODO: factor out
iseq = (self._rawobject == other._rawobject) and (self.cppclass ==
other.cppclass)
return self.space.wrap(iseq)
@@ -907,6 +927,29 @@
return self.space.w_False
return self.space.w_True
+ def instance__len__(self):
+ w_as_builtin = self._get_as_builtin()
+ if w_as_builtin is not None:
+ return self.space.len(w_as_builtin)
+ raise OperationError(
+ self.space.w_TypeError,
+ self.space.wrap("'%s' has no length" % self.cppclass.name))
+
+ def instance__cmp__(self, w_other):
+ w_as_builtin = self._get_as_builtin()
+ if w_as_builtin is not None:
+ return self.space.cmp(w_as_builtin, w_other)
+ raise OperationError(
+ self.space.w_AttributeError,
+ self.space.wrap("'%s' has no attribute __cmp__" %
self.cppclass.name))
+
+ def instance__repr__(self):
+ w_as_builtin = self._get_as_builtin()
+ if w_as_builtin is not None:
+ return self.space.repr(w_as_builtin)
+ return self.space.wrap("<%s object at 0x%x>" %
+ (self.cppclass.name, rffi.cast(rffi.ULONG,
self.get_rawobject())))
+
def destruct(self):
assert isinstance(self, W_CPPInstance)
if self._rawobject and not self.isref:
@@ -926,6 +969,9 @@
__eq__ = interp2app(W_CPPInstance.instance__eq__),
__ne__ = interp2app(W_CPPInstance.instance__ne__),
__nonzero__ = interp2app(W_CPPInstance.instance__nonzero__),
+ __len__ = interp2app(W_CPPInstance.instance__len__),
+ __cmp__ = interp2app(W_CPPInstance.instance__cmp__),
+ __repr__ = interp2app(W_CPPInstance.instance__repr__),
destruct = interp2app(W_CPPInstance.destruct),
)
W_CPPInstance.typedef.acceptable_as_base_class = True
diff --git a/pypy/module/cppyy/pythonify.py b/pypy/module/cppyy/pythonify.py
--- a/pypy/module/cppyy/pythonify.py
+++ b/pypy/module/cppyy/pythonify.py
@@ -311,13 +311,16 @@
except KeyError:
pass
+ # general note: use 'in pyclass.__dict__' rather than 'hasattr' to prevent
+ # adding pythonizations multiple times in derived classes
+
# map size -> __len__ (generally true for STL)
- if hasattr(pyclass, 'size') and \
- not hasattr(pyclass, '__len__') and callable(pyclass.size):
+ if 'size' in pyclass.__dict__ and not '__len__' in pyclass.__dict__ \
+ and callable(pyclass.size):
pyclass.__len__ = pyclass.size
# map push_back -> __iadd__ (generally true for STL)
- if hasattr(pyclass, 'push_back') and not hasattr(pyclass, '__iadd__'):
+ if 'push_back' in pyclass.__dict__ and not '__iadd__' in pyclass.__dict__:
def __iadd__(self, ll):
[self.push_back(x) for x in ll]
return self
@@ -327,7 +330,7 @@
# not on vector, for which otherwise the user has to make sure that the
# global == and != for its iterators are reflected, which is a hassle ...
if not 'vector' in pyclass.__name__[:11] and \
- (hasattr(pyclass, 'begin') and hasattr(pyclass, 'end')):
+ ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__):
# TODO: check return type of begin() and end() for existence
def __iter__(self):
iter = self.begin()
@@ -339,9 +342,9 @@
pyclass.__iter__ = __iter__
# combine __getitem__ and __len__ to make a pythonized __getitem__
- if hasattr(pyclass, '__getitem__') and hasattr(pyclass, '__len__'):
+ if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__:
pyclass._getitem__unchecked = pyclass.__getitem__
- if hasattr(pyclass, '__setitem__') and hasattr(pyclass, '__iadd__'):
+ if '__setitem__' in pyclass.__dict__ and '__iadd__' in
pyclass.__dict__:
pyclass.__getitem__ = python_style_sliceable_getitem
else:
pyclass.__getitem__ = python_style_getitem
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
@@ -103,6 +103,29 @@
def setup_class(cls):
cls.space = space
+ def test01_strings(self):
+ """Test TString/TObjString compatibility"""
+
+ import cppyy
+
+ pyteststr = "aap noot mies"
+ def test_string(s1, s2):
+ assert len(s1) == len(s2)
+ assert s1 == s1
+ assert s1 == s2
+ assert s1 == str(s1)
+ assert s1 == pyteststr
+ assert s1 != "aap"
+ assert s1 != ""
+ assert s1 < "noot"
+ assert repr(s1) == repr(s2)
+
+ s1 = cppyy.gbl.TString(pyteststr)
+ test_string(s1, pyteststr)
+
+ s3 = cppyy.gbl.TObjString(pyteststr)
+ test_string(s3, pyteststr)
+
def test03_TVector(self):
"""Test TVector2/3/T behavior"""
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit