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

Reply via email to