Author: Wim Lavrijsen <wlavrij...@lbl.gov>
Branch: cppyy-packaging
Changeset: r92722:b547e0ca6aba
Date: 2017-10-11 11:34 -0700
http://bitbucket.org/pypy/pypy/changeset/b547e0ca6aba/

Log:    naming consistency and equivalence with CPython

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
@@ -7,7 +7,7 @@
     interpleveldefs = {
         '_resolve_name'          : 'interp_cppyy.resolve_name',
         '_scope_byname'          : 'interp_cppyy.scope_byname',
-        '_template_byname'       : 'interp_cppyy.template_byname',
+        '_is_template'           : 'interp_cppyy.is_template',
         '_std_string_name'       : 'interp_cppyy.std_string_name',
         '_set_class_generator'   : 'interp_cppyy.set_class_generator',
         '_set_function_generator': 'interp_cppyy.set_function_generator',
@@ -15,6 +15,7 @@
         '_get_nullptr'           : 'interp_cppyy.get_nullptr',
         'CPPClassBase'           : 'interp_cppyy.W_CPPClass',
         'addressof'              : 'interp_cppyy.addressof',
+        '_bind_object'           : 'interp_cppyy._bind_object',
         'bind_object'            : 'interp_cppyy.bind_object',
     }
 
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
@@ -495,9 +495,9 @@
     def _unwrap_object(self, space, w_obj):
         from pypy.module._cppyy.interp_cppyy import W_CPPClass
         if isinstance(w_obj, W_CPPClass):
-            if capi.c_is_subtype(space, w_obj.cppclass, self.clsdecl):
+            if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl):
                 rawobject = w_obj.get_rawobject()
-                offset = capi.c_base_offset(space, w_obj.cppclass, 
self.clsdecl, rawobject, 1)
+                offset = capi.c_base_offset(space, w_obj.clsdecl, 
self.clsdecl, rawobject, 1)
                 obj_address = capi.direct_ptradd(rawobject, offset)
                 return rffi.cast(capi.C_OBJECT, obj_address)
         raise oefmt(space.w_TypeError,
@@ -527,7 +527,7 @@
     def from_memory(self, space, w_obj, w_pycppclass, offset):
         address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, 
offset))
         from pypy.module._cppyy import interp_cppyy
-        return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, 
do_cast=False)
+        return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, 
do_cast=False)
 
     def to_memory(self, space, w_obj, w_value, offset):
         self._is_abstract(space)
@@ -548,7 +548,7 @@
     def from_memory(self, space, w_obj, w_pycppclass, offset):
         address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, 
offset))
         from pypy.module._cppyy import interp_cppyy
-        return interp_cppyy.wrap_cppobject(space, address, self.clsdecl, 
do_cast=False)
+        return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, 
do_cast=False)
 
     def to_memory(self, space, w_obj, w_value, offset):
         address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, 
offset))
@@ -582,8 +582,8 @@
     def from_memory(self, space, w_obj, w_pycppclass, offset):
         address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, 
offset))
         from pypy.module._cppyy import interp_cppyy
-        return interp_cppyy.wrap_cppobject(space, address, self.clsdecl,
-                                           do_cast=False, is_ref=True)
+        return interp_cppyy.wrap_cppinstance(
+            space, address, self.clsdecl, do_cast=False, is_ref=True)
 
 class StdStringConverter(InstanceConverter):
 
@@ -606,7 +606,7 @@
             assign = self.clsdecl.get_overload("__assign__")
             from pypy.module._cppyy import interp_cppyy
             assign.call(
-                interp_cppyy.wrap_cppobject(space, address, self.clsdecl, 
do_cast=False), [w_value])
+                interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, 
do_cast=False), [w_value])
         except Exception:
             InstanceConverter.to_memory(self, space, w_obj, w_value, offset)
 
diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py
--- a/pypy/module/_cppyy/executor.py
+++ b/pypy/module/_cppyy/executor.py
@@ -159,7 +159,7 @@
         from pypy.module._cppyy import interp_cppyy
         long_result = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
         ptr_result = rffi.cast(capi.C_OBJECT, long_result)
-        pyres = interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass)
+        pyres = interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass)
         return pyres
 
     def execute_libffi(self, space, cif_descr, funcaddr, buffer):
@@ -167,7 +167,7 @@
         result = rffi.ptradd(buffer, cif_descr.exchange_result)
         from pypy.module._cppyy import interp_cppyy
         ptr_result = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, 
result)[0])
-        return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass)
+        return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass)
 
 class InstancePtrPtrExecutor(InstancePtrExecutor):
 
@@ -176,7 +176,7 @@
         voidp_result = capi.c_call_r(space, cppmethod, cppthis, num_args, args)
         ref_address = rffi.cast(rffi.VOIDPP, voidp_result)
         ptr_result = rffi.cast(capi.C_OBJECT, ref_address[0])
-        return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass)
+        return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass)
 
     def execute_libffi(self, space, cif_descr, funcaddr, buffer):
         from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
@@ -188,8 +188,8 @@
         from pypy.module._cppyy import interp_cppyy
         long_result = capi.c_call_o(space, cppmethod, cppthis, num_args, args, 
self.cppclass)
         ptr_result = rffi.cast(capi.C_OBJECT, long_result)
-        return interp_cppyy.wrap_cppobject(space, ptr_result, self.cppclass,
-                                           do_cast=False, python_owns=True, 
fresh=True)
+        return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass,
+                                             do_cast=False, python_owns=True, 
fresh=True)
 
     def execute_libffi(self, space, cif_descr, funcaddr, buffer):
         from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
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
@@ -2,7 +2,7 @@
 
 from pypy.interpreter.error import OperationError, oefmt
 from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.typedef import TypeDef, GetSetProperty, 
interp_attrproperty, interp_attrproperty_w
+from pypy.interpreter.typedef import TypeDef, GetSetProperty, 
interp_attrproperty
 from pypy.interpreter.baseobjspace import W_Root
 
 from rpython.rtyper.lltypesystem import rffi, lltype, llmemory
@@ -33,16 +33,21 @@
 
 class State(object):
     def __init__(self, space):
+        # final scoped name -> opaque handle
         self.cppscope_cache = {
-            "void" : W_CPPClassDecl(space, "void", capi.C_NULL_TYPE) }
+            'void' : W_CPPClassDecl(space, capi.C_NULL_TYPE, 'void') }
+        # opaque handle -> app-level python class
+        self.cppclass_registry = {}
+        # app-level class generator callback
+        self.w_clgen_callback = None
+        # app-level function generator callback (currently not used)
+        self.w_fngen_callback = None
+        # C++11's nullptr
         self.w_nullptr = None
-        self.cpptemplate_cache = {}
-        self.cppclass_registry = {}
-        self.w_clgen_callback = None
-        self.w_fngen_callback = None
 
 def get_nullptr(space):
-    if hasattr(space, "fake"):
+    # construct a unique address that compares to NULL, serves as nullptr
+    if hasattr(space, 'fake'):
         raise NotImplementedError
     state = space.fromcache(State)
     if state.w_nullptr is None:
@@ -58,52 +63,48 @@
         state.w_nullptr = nullarr
     return state.w_nullptr
 
-@unwrap_spec(name='text')
-def resolve_name(space, name):
-    return space.newtext(capi.c_resolve_name(space, name))
+@unwrap_spec(scoped_name='text')
+def resolve_name(space, scoped_name):
+    return space.newtext(capi.c_resolve_name(space, scoped_name))
 
-@unwrap_spec(name='text')
-def scope_byname(space, name):
-    true_name = capi.c_resolve_name(space, name)
 
+# memoized lookup of handles by final, scoped, name of classes/namespaces
+@unwrap_spec(final_scoped_name='text')
+def scope_byname(space, final_scoped_name):
     state = space.fromcache(State)
     try:
-        return state.cppscope_cache[true_name]
+        return state.cppscope_cache[final_scoped_name]
     except KeyError:
         pass
 
-    opaque_handle = capi.c_get_scope_opaque(space, true_name)
+    opaque_handle = capi.c_get_scope_opaque(space, final_scoped_name)
     assert lltype.typeOf(opaque_handle) == capi.C_SCOPE
     if opaque_handle:
-        final_name = capi.c_final_name(space, opaque_handle)
-        if capi.c_is_namespace(space, opaque_handle):
-            cppscope = W_CPPNamespaceDecl(space, final_name, opaque_handle)
-        elif capi.c_has_complex_hierarchy(space, opaque_handle):
-            cppscope = W_CPPComplexClassDecl(space, final_name, opaque_handle)
+        isns = capi.c_is_namespace(space, opaque_handle)
+        if isns:
+            cppscope = W_CPPNamespaceDecl(space, opaque_handle, 
final_scoped_name)
         else:
-            cppscope = W_CPPClassDecl(space, final_name, opaque_handle)
-        state.cppscope_cache[name] = cppscope
+            if capi.c_has_complex_hierarchy(space, opaque_handle):
+                cppscope = W_CPPComplexClassDecl(space, opaque_handle, 
final_scoped_name)
+            else:
+                cppscope = W_CPPClassDecl(space, opaque_handle, 
final_scoped_name)
 
-        cppscope._build_methods()
-        cppscope._find_datamembers()
+        # store in the cache to prevent recursion
+        state.cppscope_cache[final_scoped_name] = cppscope
+
+        if not isns:
+            # build methods/data; TODO: also defer this for classes (a 
functional __dir__
+            # and instrospection for help() is enough and allows more lazy 
loading)
+            cppscope._build_methods()
+            cppscope._find_datamembers()
+
         return cppscope
 
     return None
 
-@unwrap_spec(name='text')
-def template_byname(space, name):
-    state = space.fromcache(State)
-    try:
-        return state.cpptemplate_cache[name]
-    except KeyError:
-        pass
-
-    if capi.c_is_template(space, name):
-        cpptemplate = W_CPPTemplateType(space, name)
-        state.cpptemplate_cache[name] = cpptemplate
-        return cpptemplate
-
-    return None
+@unwrap_spec(final_scoped_name='text')
+def is_template(space, final_scoped_name):
+    return space.wrap(capi.c_is_template(space, final_scoped_name))
 
 def std_string_name(space):
     return space.newtext(capi.std_string_name)
@@ -591,15 +592,17 @@
     @jit.unroll_safe
     @unwrap_spec(args_w='args_w')
     def call(self, w_cppinstance, args_w):
+        # TODO: factor out the following:
+        if capi.c_is_abstract(self.space, self.scope.handle):
+            raise oefmt(self.space.w_TypeError,
+                        "cannot instantiate abstract class '%s'",
+                        self.scope.name)
         w_result = W_CPPOverload.call(self, w_cppinstance, args_w)
         newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result))
         cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, 
can_be_None=True)
         if cppinstance is not None:
             cppinstance._rawobject = newthis
             memory_regulator.register(cppinstance)
-            return w_cppinstance
-        return wrap_cppobject(self.space, newthis, self.functions[0].scope,
-                              do_cast=False, python_owns=True, fresh=True)
 
     def __repr__(self):
         return "W_CPPConstructorOverload(%s)" % [f.signature() for f in 
self.functions]
@@ -643,8 +646,8 @@
 
     def _get_offset(self, cppinstance):
         if cppinstance:
-            assert lltype.typeOf(cppinstance.cppclass.handle) == 
lltype.typeOf(self.scope.handle)
-            offset = self.offset + 
cppinstance.cppclass.get_base_offset(cppinstance, self.scope)
+            assert lltype.typeOf(cppinstance.clsdecl.handle) == 
lltype.typeOf(self.scope.handle)
+            offset = self.offset + 
cppinstance.clsdecl.get_base_offset(cppinstance, self.scope)
         else:
             offset = self.offset
         return offset
@@ -705,12 +708,12 @@
         return space.w_False
 
 class W_CPPScopeDecl(W_Root):
-    _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers']
+    _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers']
     _immutable_fields_ = ['handle', 'name']
 
-    def __init__(self, space, name, opaque_handle):
+    def __init__(self, space, opaque_handle, final_scoped_name):
         self.space = space
-        self.name = name
+        self.name = final_scoped_name
         assert lltype.typeOf(opaque_handle) == capi.C_SCOPE
         self.handle = opaque_handle
         self.methods = {}
@@ -769,6 +772,9 @@
 # classes for inheritance. Both are python classes, though, and refactoring
 # may be in order at some point.
 class W_CPPNamespaceDecl(W_CPPScopeDecl):
+    _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers']
+    _immutable_fields_ = ['handle', 'name']
+
     def _make_cppfunction(self, pyname, index):
         num_args = capi.c_method_num_args(self.space, self, index)
         args_required = capi.c_method_req_args(self.space, self, index)
@@ -779,9 +785,6 @@
             arg_defs.append((arg_type, arg_dflt))
         return CPPFunction(self.space, self, index, arg_defs, args_required)
 
-    def _build_methods(self):
-        pass       # force lazy lookups in namespaces
-
     def _make_datamember(self, dm_name, dm_idx):
         type_name = capi.c_datamember_type(self.space, self, dm_idx)
         offset = capi.c_datamember_offset(self.space, self, dm_idx)
@@ -791,9 +794,6 @@
         self.datamembers[dm_name] = datamember
         return datamember
 
-    def _find_datamembers(self):
-        pass       # force lazy lookups in namespaces
-
     def find_overload(self, meth_name):
         indices = capi.c_method_indices_from_name(self.space, self, meth_name)
         if not indices:
@@ -855,18 +855,21 @@
 
 
 class W_CPPClassDecl(W_CPPScopeDecl):
-    _attrs_ = ['space', 'name', 'handle', 'methods', 'datamembers']
-    _immutable_fields_ = ['handle', 'constructor', 'methods[*]', 
'datamembers[*]']
+    _attrs_ = ['space', 'handle', 'name', 'methods', 'datamembers']
+    _immutable_fields_ = ['handle', 'name', 'methods[*]', 'datamembers[*]']
 
     def _build_methods(self):
         assert len(self.methods) == 0
         methods_temp = {}
         for i in range(capi.c_num_methods(self.space, self)):
             idx = capi.c_method_index_at(self.space, self, i)
-            pyname = helper.map_operator_name(self.space,
-                capi.c_method_name(self.space, self, idx),
-                capi.c_method_num_args(self.space, self, idx),
-                capi.c_method_result_type(self.space, self, idx))
+            if capi.c_is_constructor(self.space, self, idx):
+                pyname = '__init__'
+            else:
+                pyname = helper.map_operator_name(self.space,
+                    capi.c_method_name(self.space, self, idx),
+                    capi.c_method_num_args(self.space, self, idx),
+                    capi.c_method_result_type(self.space, self, idx))
             cppmethod = self._make_cppfunction(pyname, idx)
             methods_temp.setdefault(pyname, []).append(cppmethod)
         # the following covers the case where the only kind of operator[](idx)
@@ -883,7 +886,7 @@
         # create the overload methods from the method sets
         for pyname, methods in methods_temp.iteritems():
             CPPMethodSort(methods).sort()
-            if pyname == self.name:
+            if pyname == '__init__':
                 overload = W_CPPConstructorOverload(self.space, self, 
methods[:])
             else:
                 overload = W_CPPOverload(self.space, self, methods[:])
@@ -934,11 +937,11 @@
         raise self.missing_attribute_error(name)
 
     def get_base_offset(self, cppinstance, calling_scope):
-        assert self == cppinstance.cppclass
+        assert self == cppinstance.clsdecl
         return 0
 
     def get_cppthis(self, cppinstance, calling_scope):
-        assert self == cppinstance.cppclass
+        assert self == cppinstance.clsdecl
         return cppinstance.get_rawobject()
 
     def is_namespace(self):
@@ -973,13 +976,13 @@
 
 class W_CPPComplexClassDecl(W_CPPClassDecl):
     def get_base_offset(self, cppinstance, calling_scope):
-        assert self == cppinstance.cppclass
+        assert self == cppinstance.clsdecl
         offset = capi.c_base_offset(self.space,
                                     self, calling_scope, 
cppinstance.get_rawobject(), 1)
         return offset
 
     def get_cppthis(self, cppinstance, calling_scope):
-        assert self == cppinstance.cppclass
+        assert self == cppinstance.clsdecl
         offset = self.get_base_offset(cppinstance, calling_scope)
         return capi.direct_ptradd(cppinstance.get_rawobject(), offset)
 
@@ -997,37 +1000,16 @@
 W_CPPComplexClassDecl.typedef.acceptable_as_base_class = False
 
 
-class W_CPPTemplateType(W_Root):
-    _attrs_ = ['space', 'name']
-    _immutable_fields = ['name']
-
-    def __init__(self, space, name):
-        self.space = space
-        self.name = name
-
-    @unwrap_spec(args_w='args_w')
-    def __call__(self, args_w):
-        # TODO: this is broken but unused (see pythonify.py)
-        fullname = "".join([self.name, '<', self.space.text_w(args_w[0]), '>'])
-        return scope_byname(self.space, fullname)
-
-W_CPPTemplateType.typedef = TypeDef(
-    'CPPTemplateType',
-    __call__ = interp2app(W_CPPTemplateType.__call__),
-)
-W_CPPTemplateType.typedef.acceptable_as_base_class = False
-
-
 class W_CPPClass(W_Root):
-    _attrs_ = ['space', 'cppclass', '_rawobject', 'isref', 'python_owns',
+    _attrs_ = ['space', 'clsdecl', '_rawobject', 'isref', 'python_owns',
                'finalizer_registered']
-    _immutable_fields_ = ["cppclass", "isref"]
+    _immutable_fields_ = ['clsdecl', 'isref']
 
     finalizer_registered = False
 
-    def __init__(self, space, cppclass, rawobject, isref, python_owns):
+    def __init__(self, space, decl, rawobject, isref, python_owns):
         self.space = space
-        self.cppclass = cppclass
+        self.clsdecl = decl
         assert lltype.typeOf(rawobject) == capi.C_OBJECT
         assert not isref or rawobject
         self._rawobject = rawobject
@@ -1057,7 +1039,7 @@
         self._opt_register_finalizer()
 
     def get_cppthis(self, calling_scope):
-        return self.cppclass.get_cppthis(self, calling_scope)
+        return self.clsdecl.get_cppthis(self, calling_scope)
 
     def get_rawobject(self):
         if not self.isref:
@@ -1078,12 +1060,9 @@
         return None
 
     def instance__init__(self, args_w):
-        if capi.c_is_abstract(self.space, self.cppclass.handle):
-            raise oefmt(self.space.w_TypeError,
-                        "cannot instantiate abstract class '%s'",
-                        self.cppclass.name)
-        constructor_overload = self.cppclass.get_overload(self.cppclass.name)
-        constructor_overload.call(self, args_w)
+        raise oefmt(self.space.w_TypeError,
+                    "cannot instantiate abstract class '%s'",
+                    self.clsdecl.name)
  
     def instance__eq__(self, w_other):
         # special case: if other is None, compare pointer-style
@@ -1099,7 +1078,7 @@
             for name in ["", "__gnu_cxx", "__1"]:
                 nss = scope_byname(self.space, name)
                 meth_idx = capi.c_get_global_operator(
-                    self.space, nss, self.cppclass, other.cppclass, 
"operator==")
+                    self.space, nss, self.clsdecl, other.clsdecl, "operator==")
                 if meth_idx != -1:
                     f = nss._make_cppfunction("operator==", meth_idx)
                     ol = W_CPPOverload(self.space, nss, [f])
@@ -1118,7 +1097,7 @@
         # 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_CPPClass, w_other, can_be_None=False)  # 
TODO: factor out
-        iseq = (self._rawobject == other._rawobject) and (self.cppclass == 
other.cppclass)
+        iseq = (self._rawobject == other._rawobject) and (self.clsdecl == 
other.clsdecl)
         return self.space.newbool(iseq)
 
     def instance__ne__(self, w_other):
@@ -1134,26 +1113,26 @@
         if w_as_builtin is not None:
             return self.space.len(w_as_builtin)
         raise oefmt(self.space.w_TypeError,
-                    "'%s' has no length", self.cppclass.name)
+                    "'%s' has no length", self.clsdecl.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 oefmt(self.space.w_AttributeError,
-                    "'%s' has no attribute __cmp__", self.cppclass.name)
+                    "'%s' has no attribute __cmp__", self.clsdecl.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.newtext("<%s object at 0x%x>" %
-                               (self.cppclass.name, rffi.cast(rffi.ULONG, 
self.get_rawobject())))
+                               (self.clsdecl.name, rffi.cast(rffi.ULONG, 
self.get_rawobject())))
 
     def destruct(self):
         if self._rawobject and not self.isref:
             memory_regulator.unregister(self)
-            capi.c_destruct(self.space, self.cppclass, self._rawobject)
+            capi.c_destruct(self.space, self.clsdecl, self._rawobject)
             self._rawobject = capi.C_NULL_OBJECT
 
     def _finalize_(self):
@@ -1162,7 +1141,6 @@
 
 W_CPPClass.typedef = TypeDef(
     'CPPClass',
-    cppclass = interp_attrproperty_w('cppclass', cls=W_CPPClass),
     _python_owns = GetSetProperty(W_CPPClass.fget_python_owns, 
W_CPPClass.fset_python_owns),
     __init__ = interp2app(W_CPPClass.instance__init__),
     __eq__ = interp2app(W_CPPClass.instance__eq__),
@@ -1220,21 +1198,21 @@
     state = space.fromcache(State)
     return space.call_function(state.w_fngen_callback, w_callable, 
space.newint(npar))
 
-def wrap_cppobject(space, rawobject, cppclass,
-                   do_cast=True, python_owns=False, is_ref=False, fresh=False):
+def wrap_cppinstance(space, rawobject, clsdecl,
+                     do_cast=True, python_owns=False, is_ref=False, 
fresh=False):
     rawobject = rffi.cast(capi.C_OBJECT, rawobject)
 
     # cast to actual if requested and possible
     w_pycppclass = None
     if do_cast and rawobject:
-        actual = capi.c_actual_class(space, cppclass, rawobject)
-        if actual != cppclass.handle:
+        actual = capi.c_actual_class(space, clsdecl, rawobject)
+        if actual != clsdecl.handle:
             try:
                 w_pycppclass = get_pythonized_cppclass(space, actual)
-                offset = capi.c_base_offset1(space, actual, cppclass, 
rawobject, -1)
+                offset = capi.c_base_offset1(space, actual, clsdecl, 
rawobject, -1)
                 rawobject = capi.direct_ptradd(rawobject, offset)
-                w_cppclass = space.findattr(w_pycppclass, 
space.newtext("__cppdecl__"))
-                cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, 
can_be_None=False)
+                w_cppdecl = space.findattr(w_pycppclass, 
space.newtext("__cppdecl__"))
+                clsdecl = space.interp_w(W_CPPClassDecl, w_cppdecl, 
can_be_None=False)
             except Exception:
                 # failed to locate/build the derived class, so stick to the 
base (note
                 # that only get_pythonized_cppclass is expected to raise, so 
none of
@@ -1242,18 +1220,18 @@
                 pass
 
     if w_pycppclass is None:
-        w_pycppclass = get_pythonized_cppclass(space, cppclass.handle)
+        w_pycppclass = get_pythonized_cppclass(space, clsdecl.handle)
 
     # try to recycle existing object if this one is not newly created
     if not fresh and rawobject:
         obj = memory_regulator.retrieve(rawobject)
-        if obj is not None and obj.cppclass is cppclass:
+        if obj is not None and obj.clsdecl is clsdecl:
             return obj
 
     # fresh creation
     w_cppinstance = space.allocate_instance(W_CPPClass, w_pycppclass)
     cppinstance = space.interp_w(W_CPPClass, w_cppinstance, can_be_None=False)
-    cppinstance.__init__(space, cppclass, rawobject, is_ref, python_owns)
+    cppinstance.__init__(space, clsdecl, rawobject, is_ref, python_owns)
     memory_regulator.register(cppinstance)
     return w_cppinstance
 
@@ -1273,19 +1251,24 @@
     return space.newlong(address)
 
 @unwrap_spec(owns=bool, cast=bool)
-def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False):
-    """Takes an address and a bound C++ class proxy, returns a bound 
instance."""
+def _bind_object(space, w_obj, w_clsdecl, owns=False, cast=False):
     try:
         # attempt address from array or C++ instance
         rawobject = rffi.cast(capi.C_OBJECT, _addressof(space, w_obj))
     except Exception:
         # accept integer value as address
         rawobject = rffi.cast(capi.C_OBJECT, space.uint_w(w_obj))
-    w_cppclass = space.findattr(w_pycppclass, space.newtext("__cppdecl__"))
-    if not w_cppclass:
-        w_cppclass = scope_byname(space, space.text_w(w_pycppclass))
-        if not w_cppclass:
+    decl = space.interp_w(W_CPPClassDecl, w_clsdecl, can_be_None=False)
+    return wrap_cppinstance(space, rawobject, decl, python_owns=owns, 
do_cast=cast)
+
+@unwrap_spec(owns=bool, cast=bool)
+def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False):
+    """Takes an address and a bound C++ class proxy, returns a bound 
instance."""
+    w_clsdecl = space.findattr(w_pycppclass, space.newtext("__cppdecl__"))
+    if not w_clsdecl:
+        w_clsdecl = scope_byname(space, space.text_w(w_pycppclass))
+        if not w_clsdecl:
             raise oefmt(space.w_TypeError,
                         "no such class: %s", space.text_w(w_pycppclass))
-    cppclass = space.interp_w(W_CPPClassDecl, w_cppclass, can_be_None=False)
-    return wrap_cppobject(space, rawobject, cppclass, do_cast=cast, 
python_owns=owns)
+    return _bind_object(space, w_obj, w_clsdecl, owns, cast)
+
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
@@ -10,7 +10,7 @@
 class CPPMetaScope(type):
     def __getattr__(self, name):
         try:
-            return get_pycppitem(self, name)  # will cache on self
+            return get_scoped_pycppitem(self, name)  # will cache on self
         except Exception as e:
             raise AttributeError("%s object has no attribute '%s' (details: 
%s)" %
                                  (self, name, str(e)))
@@ -36,11 +36,14 @@
             self._scope = scope
 
     def _arg_to_str(self, arg):
-        if arg == str:
-            import _cppyy
-            arg = _cppyy._std_string_name()
-        elif type(arg) != str:
-            arg = arg.__name__
+        try:
+            arg = arg.__cppname__
+        except AttributeError:
+            if arg == str:
+                import _cppyy
+                arg = _cppyy._std_string_name()
+            elif type(arg) != str:
+                arg = arg.__name__
         return arg
 
     def __call__(self, *args):
@@ -58,8 +61,36 @@
         return self.__call__(*args)
 
 
-def clgen_callback(name):
-    return get_pycppclass(name)
+def scope_splitter(name):
+    is_open_template, scope = 0, ""
+    for c in name:
+        if c == ':' and not is_open_template:
+            if scope:
+                yield scope
+                scope = ""
+            continue
+        elif c == '<':
+            is_open_template += 1
+        elif c == '>':
+            is_open_template -= 1
+        scope += c
+    yield scope
+
+def get_pycppitem(final_scoped_name):
+    # walk scopes recursively down from global namespace ("::") to get the
+    # actual (i.e. not typedef'ed) class, triggering all necessary creation
+    scope = gbl
+    for name in scope_splitter(final_scoped_name):
+        scope = getattr(scope, name)
+    return scope
+get_pycppclass = get_pycppitem     # currently no distinction, but might
+                                   # in future for performance
+
+
+# callbacks (originating from interp_cppyy.py) to allow interp-level to
+# initiate creation of app-level classes and function
+def clgen_callback(final_scoped_name):
+    return get_pycppclass(final_scoped_name)
 
 def fngen_callback(func, npar): # todo, some kind of arg transform spec
     if npar == 0:
@@ -75,6 +106,12 @@
         return wrapper
 
 
+# construction of namespaces and classes, and their helpers
+def make_module_name(scope):
+    if scope:
+        return scope.__module__ + '.' + scope.__name__
+    return 'cppyy'
+
 def make_static_function(func_name, cppol):
     def function(*args):
         return cppol.call(None, *args)
@@ -82,13 +119,6 @@
     function.__doc__ = cppol.signature()
     return staticmethod(function)
 
-def make_method(meth_name, cppol):
-    def method(self, *args):
-        return cppol.call(self, *args)
-    method.__name__ = meth_name
-    method.__doc__ = cppol.signature()
-    return method
-
 
 def make_cppnamespace(scope, name, decl):
     # build up a representation of a C++ namespace (namespaces are classes)
@@ -98,20 +128,19 @@
     ns_meta = type(name+'_meta', (CPPMetaNamespace,), {})
 
     # create the python-side C++ namespace representation, cache in scope if 
given
-    d = {"__cppdecl__" : decl, "__cppname__" : decl.__cppname__ }
+    d = {"__cppdecl__" : decl,
+         "__module__" : make_module_name(scope),
+         "__cppname__" : decl.__cppname__ }
     pyns = ns_meta(name, (CPPNamespace,), d)
     if scope:
         setattr(scope, name, pyns)
 
     # install as modules to allow importing from (note naming: cppyy)
-    modname = 'cppyy.gbl'
-    if scope:
-        modname = 'cppyy.gbl.'+pyns.__cppname__.replace('::', '.')
-    sys.modules[modname] = pyns
+    sys.modules[make_module_name(pyns)] = pyns
     return pyns
 
 def _drop_cycles(bases):
-    # TODO: figure this out, as it seems to be a PyPy bug?!
+    # TODO: figure out why this is necessary?
     for b1 in bases:
         for b2 in bases:
             if not (b1 is b2) and issubclass(b2, b1):
@@ -119,27 +148,37 @@
                 break
     return tuple(bases)
 
-def make_new(class_name):
+
+def make_new(decl):
     def __new__(cls, *args):
         # create a place-holder only as there may be a derived class defined
+        # TODO: get rid of the import and add user-land bind_object that uses
+        # _bind_object (see interp_cppyy.py)
         import _cppyy
-        instance = _cppyy.bind_object(0, class_name, True)
+        instance = _cppyy._bind_object(0, decl, True)
         if not instance.__class__ is cls:
             instance.__class__ = cls     # happens for derived class
         return instance
     return __new__
 
-def make_cppclass(scope, class_name, final_class_name, decl):
+def make_method(meth_name, cppol):
+    def method(self, *args):
+        return cppol.call(self, *args)
+    method.__name__ = meth_name
+    method.__doc__ = cppol.signature()
+    return method
+
+def make_cppclass(scope, cl_name, decl):
 
     # get a list of base classes for class creation
     bases = [get_pycppclass(base) for base in decl.get_base_names()]
     if not bases:
         bases = [CPPClass,]
     else:
-        # it's technically possible that the required class now has been built
-        # if one of the base classes uses it in e.g. a function interface
+        # it's possible that the required class now has been built if one of
+        # the base classes uses it in e.g. a function interface
         try:
-            return scope.__dict__[final_class_name]
+            return scope.__dict__[cl_name]
         except KeyError:
             pass
 
@@ -147,39 +186,41 @@
     d_meta = {}
 
     # prepare dictionary for python-side C++ class representation
-    def dispatch(self, name, signature):
-        cppol = decl.dispatch(name, signature)
-        return types.MethodType(make_method(name, cppol), self, type(self))
+    def dispatch(self, m_name, signature):
+        cppol = decl.__dispatch__(m_name, signature)
+        return types.MethodType(make_method(m_name, cppol), self, type(self))
     d_class = {"__cppdecl__"   : decl,
+         "__new__"      : make_new(decl),
+         "__module__"   : make_module_name(scope),
          "__cppname__"  : decl.__cppname__,
-         "__new__"      : make_new(class_name),
+         "__dispatch__" : dispatch,
          }
 
     # insert (static) methods into the class dictionary
-    for name in decl.get_method_names():
-        cppol = decl.get_overload(name)
+    for m_name in decl.get_method_names():
+        cppol = decl.get_overload(m_name)
         if cppol.is_static():
-            d_class[name] = make_static_function(name, cppol)
+            d_class[m_name] = make_static_function(m_name, cppol)
         else:
-            d_class[name] = make_method(name, cppol)
+            d_class[m_name] = make_method(m_name, cppol)
 
     # add all data members to the dictionary of the class to be created, and
     # static ones also to the metaclass (needed for property setters)
-    for name in decl.get_datamember_names():
-        cppdm = decl.get_datamember(name)
-        d_class[name] = cppdm
+    for d_name in decl.get_datamember_names():
+        cppdm = decl.get_datamember(d_name)
+        d_class[d_name] = cppdm
         if cppdm.is_static():
-            d_meta[name] = cppdm
+            d_meta[d_name] = cppdm
 
     # create a metaclass to allow properties (for static data write access)
     metabases = [type(base) for base in bases]
-    metacpp = type(CPPMetaScope)(class_name+'_meta', _drop_cycles(metabases), 
d_meta)
+    metacpp = type(CPPMetaScope)(cl_name+'_meta', _drop_cycles(metabases), 
d_meta)
 
     # create the python-side C++ class
-    pycls = metacpp(class_name, _drop_cycles(bases), d_class)
+    pycls = metacpp(cl_name, _drop_cycles(bases), d_class)
 
     # store the class on its outer scope
-    setattr(scope, final_class_name, pycls)
+    setattr(scope, cl_name, pycls)
 
     # the call to register will add back-end specific pythonizations and thus
     # needs to run first, so that the generic pythonizations can use them
@@ -192,32 +233,32 @@
     return CPPTemplate(template_name, scope)
 
 
-def get_pycppitem(scope, name):
+def get_scoped_pycppitem(scope, name):
     import _cppyy
 
-    # resolve typedefs/aliases
-    full_name = (scope == gbl) and name or (scope.__name__+'::'+name)
-    true_name = _cppyy._resolve_name(full_name)
-    if true_name != full_name:
-        return get_pycppclass(true_name)
+    # resolve typedefs/aliases: these may cross namespaces, in which case
+    # the lookup must trigger the creation of all necessary scopes
+    scoped_name = (scope == gbl) and name or (scope.__cppname__+'::'+name)
+    final_scoped_name = _cppyy._resolve_name(scoped_name)
+    if final_scoped_name != scoped_name:
+        pycppitem = get_pycppitem(final_scoped_name)
+        # also store on the requested scope (effectively a typedef or pointer 
copy)
+        setattr(scope, name, pycppitem)
+        return pycppitem
 
     pycppitem = None
 
-    # classes
-    cppitem = _cppyy._scope_byname(true_name)
+    # scopes (classes and namespaces)
+    cppitem = _cppyy._scope_byname(final_scoped_name)
     if cppitem:
-        name = true_name
-        if scope != gbl:
-            name = true_name[len(scope.__cppname__)+2:]
         if cppitem.is_namespace():
             pycppitem = make_cppnamespace(scope, name, cppitem)
-            setattr(scope, name, pycppitem)
         else:
-            pycppitem = make_cppclass(scope, name, true_name, cppitem)
+            pycppitem = make_cppclass(scope, name, cppitem)
 
     # templates
     if not cppitem:
-        cppitem = _cppyy._template_byname(true_name)
+        cppitem = _cppyy._is_template(final_scoped_name)
         if cppitem:
             pycppitem = make_cpptemplatetype(scope, name)
             setattr(scope, name, pycppitem)
@@ -249,29 +290,6 @@
     raise AttributeError("'%s' has no attribute '%s'" % (str(scope), name))
 
 
-def scope_splitter(name):
-    is_open_template, scope = 0, ""
-    for c in name:
-        if c == ':' and not is_open_template:
-            if scope:
-                yield scope
-                scope = ""
-            continue
-        elif c == '<':
-            is_open_template += 1
-        elif c == '>':
-            is_open_template -= 1
-        scope += c
-    yield scope
-
-def get_pycppclass(name):
-    # break up the name, to walk the scopes and get the class recursively
-    scope = gbl
-    for part in scope_splitter(name):
-        scope = getattr(scope, part)
-    return scope
-
-
 # pythonization by decoration (move to their own file?)
 def python_style_getitem(self, idx):
     # python-style indexing: check for size and allow indexing from the back
@@ -346,8 +364,8 @@
     # also the fallback on the indexed __getitem__, but that is slower)
     if not 'vector' in pyclass.__name__[:11] and \
             ('begin' in pyclass.__dict__ and 'end' in pyclass.__dict__):
-        if _cppyy._scope_byname(pyclass.__name__+'::iterator') or \
-                _cppyy._scope_byname(pyclass.__name__+'::const_iterator'):
+        if _cppyy._scope_byname(pyclass.__cppname__+'::iterator') or \
+                _cppyy._scope_byname(pyclass.__cppname__+'::const_iterator'):
             def __iter__(self):
                 i = self.begin()
                 while i != self.end():
diff --git a/pypy/module/_cppyy/test/test_cppyy.py 
b/pypy/module/_cppyy/test/test_cppyy.py
--- a/pypy/module/_cppyy/test/test_cppyy.py
+++ b/pypy/module/_cppyy/test/test_cppyy.py
@@ -31,12 +31,16 @@
     spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools'])
 
     def setup_class(cls):
-        cls.w_lib, cls.w_example01, cls.w_payload = \
+        cls.w_lib, cls.w_instantiate, cls.w_example01, cls.w_payload = \
                    cls.space.unpackiterable(cls.space.appexec([], """():
             import _cppyy, ctypes
             lib = ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)
-            return lib, _cppyy._scope_byname('example01'), 
_cppyy._scope_byname('payload')"""\
-                                                              % (test_dct, )))
+            def cpp_instantiate(tt, *args):
+                inst = _cppyy._bind_object(0, tt, True)
+                tt.get_overload("__init__").call(inst, *args)
+                return inst
+            return lib, cpp_instantiate, _cppyy._scope_byname('example01'),\
+                          _cppyy._scope_byname('payload')""" % (test_dct, )))
 
     def test01_static_int(self):
         """Test passing of an int, returning of an int, and overloading on a
@@ -95,7 +99,7 @@
 
         assert t.get_overload("getCount").call(None) == 0
 
-        e1 = t.get_overload(t.__cppname__).call(None, 7)
+        e1 = self.instantiate(t, 7)
         assert t.get_overload("getCount").call(None) == 1
         res = t.get_overload("addDataToInt").call(e1, 4)
         assert res == 11
@@ -105,8 +109,8 @@
         assert t.get_overload("getCount").call(None) == 0
         raises(ReferenceError, 't.get_overload("addDataToInt").call(e1, 4)')
 
-        e1 = t.get_overload(t.__cppname__).call(None, 7)
-        e2 = t.get_overload(t.__cppname__).call(None, 8)
+        e1 = self.instantiate(t, 7)
+        e2 = self.instantiate(t, 8)
         assert t.get_overload("getCount").call(None) == 2
         e1.__destruct__()
         assert t.get_overload("getCount").call(None) == 1
@@ -128,7 +132,7 @@
 
         assert t.get_overload("getCount").call(None) == 0
 
-        e1 = t.get_overload(t.__cppname__).call(None, 7)
+        e1 = self.instantiate(t, 7)
         assert t.get_overload("getCount").call(None) == 1
         res = t.get_overload("addDataToInt").call(e1, 4)
         assert res == 11
@@ -138,8 +142,8 @@
         gc.collect()
         assert t.get_overload("getCount").call(None) == 0
 
-        e1 = t.get_overload(t.__cppname__).call(None, 7)
-        e2 = t.get_overload(t.__cppname__).call(None, 8)
+        e1 = self.instantiate(t, 7)
+        e2 = self.instantiate(t, 8)
         assert t.get_overload("getCount").call(None) == 2
         e1 = None
         gc.collect()
@@ -159,7 +163,7 @@
 
         assert t.get_overload("getCount").call(None) == 0
 
-        e1 = t.get_overload(t.__cppname__).call(None, 7)
+        e1 = self.instantiate(t, 7)
         assert t.get_overload("getCount").call(None) == 1
         assert e1._python_owns == True
         e1._python_owns = False
@@ -178,12 +182,12 @@
 
         t = self.example01
 
-        e = t.get_overload(t.__cppname__).call(None, 13)
+        e = self.instantiate(t, 13)
         res = t.get_overload("addDataToDouble").call(e, 16)
         assert round(res-29, 8) == 0.
         e.__destruct__()
 
-        e = t.get_overload(t.__cppname__).call(None, -13)
+        e = self.instantiate(t, -13)
         res = t.get_overload("addDataToDouble").call(e, 16)
         assert round(res-3, 8) == 0.
         e.__destruct__()
@@ -196,7 +200,7 @@
 
         t = self.example01
 
-        e = t.get_overload(t.__cppname__).call(None, 42)
+        e = self.instantiate(t, 42)
         res = t.get_overload("addDataToAtoi").call(e, "13")
         assert res == 55
         res = t.get_overload("addToStringValue").call(e, "12")       # TODO: 
this leaks
@@ -213,12 +217,12 @@
         t1 = self.example01
         t2 = self.payload
 
-        pl = t2.get_overload(t2.__cppname__).call(None, 3.14)
+        pl = self.instantiate(t2, 3.14)
         assert round(t2.get_overload("getData").call(pl)-3.14, 8) == 0
         t1.get_overload("staticSetPayload").call(None, pl, 41.)
         assert t2.get_overload("getData").call(pl) == 41.
 
-        e = t1.get_overload(t1.__cppname__).call(None, 50)
+        e = self.instantiate(t1, 50)
         t1.get_overload("setPayload").call(e, pl);
         assert round(t2.get_overload("getData").call(pl)-50., 8) == 0
 
@@ -233,12 +237,12 @@
         t1 = self.example01
         t2 = self.payload
 
-        pl1 = t2.get_overload(t2.__cppname__).call(None, 3.14)
+        pl1 = self.instantiate(t2, 3.14)
         assert round(t2.get_overload("getData").call(pl1)-3.14, 8) == 0
         pl2 = t1.get_overload("staticCyclePayload").call(None, pl1, 38.)
         assert t2.get_overload("getData").call(pl2) == 38.
 
-        e = t1.get_overload(t1.__cppname__).call(None, 50)
+        e = self.instantiate(t1, 50)
         pl2 = t1.get_overload("cyclePayload").call(e, pl1);
         assert round(t2.get_overload("getData").call(pl2)-50., 8) == 0
 
diff --git a/pypy/module/_cppyy/test/test_fragile.py 
b/pypy/module/_cppyy/test/test_fragile.py
--- a/pypy/module/_cppyy/test/test_fragile.py
+++ b/pypy/module/_cppyy/test/test_fragile.py
@@ -189,7 +189,7 @@
             o = fragile.O()       # raises TypeError
             assert 0
         except TypeError as e:
-            assert "cannot instantiate abstract class 'O'" in str(e)
+            assert "cannot instantiate abstract class 'fragile::O'" in str(e)
 
     def test10_dir(self):
         """Test __dir__ method"""
@@ -239,7 +239,7 @@
         assert _cppyy.gbl.fragile.nested1 is nested1
         assert nested1.__name__ == 'nested1'
         assert nested1.__module__ == 'cppyy.gbl.fragile'
-        assert nested1.__cppname__ == 'nested1'
+        assert nested1.__cppname__ == 'fragile::nested1'
 
         from cppyy.gbl.fragile.nested1 import A, nested2
         assert _cppyy.gbl.fragile.nested1.A is A
@@ -254,17 +254,17 @@
         from cppyy.gbl.fragile.nested1.nested2 import A, nested3
         assert _cppyy.gbl.fragile.nested1.nested2.A is A
         assert A.__name__ == 'A'
-        assert A.__module__ == 'cppyy.gbl.fragile.nested1'
+        assert A.__module__ == 'cppyy.gbl.fragile.nested1.nested2'
         assert A.__cppname__ == 'fragile::nested1::nested2::A'
         assert _cppyy.gbl.fragile.nested1.nested2.nested3 is nested3
         assert A.__name__ == 'A'
-        assert A.__module__ == 'cppyy.gbl.fragile.nested1'
+        assert A.__module__ == 'cppyy.gbl.fragile.nested1.nested2'
         assert nested3.__cppname__ == 'fragile::nested1::nested2::nested3'
 
         from cppyy.gbl.fragile.nested1.nested2.nested3 import A
         assert _cppyy.gbl.fragile.nested1.nested2.nested3.A is nested3.A
         assert A.__name__ == 'A'
-        assert A.__module__ == 'cppyy.gbl.fragile.nested1'
+        assert A.__module__ == 'cppyy.gbl.fragile.nested1.nested2.nested3'
         assert A.__cppname__ == 'fragile::nested1::nested2::nested3::A'
 
     def test12_missing_casts(self):
diff --git a/pypy/module/_cppyy/test/test_zjit.py 
b/pypy/module/_cppyy/test/test_zjit.py
--- a/pypy/module/_cppyy/test/test_zjit.py
+++ b/pypy/module/_cppyy/test/test_zjit.py
@@ -279,7 +279,8 @@
         drv = jit.JitDriver(greens=[], reds=["i", "inst", "cppmethod"])
         def f():
             cls  = interp_cppyy.scope_byname(space, "example01")
-            inst = cls.get_overload("example01").call(None, [FakeInt(0)])
+            inst = interp_cppyy._bind_object(space, FakeInt(0), cls, True)
+            cls.get_overload("__init__").call(inst, [FakeInt(0)])
             cppmethod = cls.get_overload(method_name)
             assert isinstance(inst, interp_cppyy.W_CPPClass)
             i = 10
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to