Author: Wim Lavrijsen <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit