Author: Armin Rigo <ar...@tunes.org> Branch: py3.5 Changeset: r94885:130fbb74dab5 Date: 2018-07-23 18:20 +0200 http://bitbucket.org/pypy/pypy/changeset/130fbb74dab5/
Log: hg merge default diff too long, truncating to 2000 out of 2216 lines diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -43,14 +43,10 @@ "_jitlog", ]) -from rpython.jit.backend import detect_cpu -try: - if detect_cpu.autodetect().startswith('x86'): - if not sys.platform.startswith('openbsd'): - working_modules.add('_vmprof') - working_modules.add('faulthandler') -except detect_cpu.ProcessorAutodetectError: - pass +import rpython.rlib.rvmprof.cintf +if rpython.rlib.rvmprof.cintf.IS_SUPPORTED: + working_modules.add('_vmprof') + working_modules.add('faulthandler') translation_modules = default_modules.copy() translation_modules.update([ @@ -323,3 +319,4 @@ parser = to_optparse(config) #, useoptions=["translation.*"]) option, args = parser.parse_args() print config + print working_modules diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -7,9 +7,12 @@ .. branch: cppyy-packaging -Upgrade to backend 1.1.0, improved handling of templated methods and +Upgrade to backend 1.2.0, improved handling of templated methods and functions (in particular automatic deduction of types), improved pythonization -interface, and a range of compatibility fixes for Python3 +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation .. branch: socket_default_timeout_blockingness diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -3935,8 +3935,8 @@ def test_char_pointer_conversion(): import warnings - assert __version__.startswith(("1.8", "1.9", "1.10", "1.11", "1.12")), ( - "consider turning the warning into an error") + assert __version__.startswith("1."), ( + "the warning will be an error if we ever release cffi 2.x") BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) 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 @@ -1,15 +1,12 @@ import sys from pypy.interpreter.error import OperationError, oefmt - from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat from rpython.rlib import rfloat, rawrefcount - from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_ArrayInstance - -from pypy.module._cppyy import helper, capi, ffitypes +from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews # Converter objects are used to translate between RPython and C++. They are # defined by the type name for which they provide conversion. Uses are for @@ -149,7 +146,8 @@ # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONG, address_value) - return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address) + return lowlevelviews.W_LowLevelView( + space, letter2tp(space, self.typecode), self.size, address) def to_memory(self, space, w_obj, w_value, offset): # copy the full array (uses byte copy for now) @@ -190,7 +188,8 @@ # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONGP, address_value) - return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address[0]) + return lowlevelviews.W_LowLevelView( + space, letter2tp(space, self.typecode), self.size, address[0]) def to_memory(self, space, w_obj, w_value, offset): # copy only the pointer value @@ -438,7 +437,7 @@ from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) shape = letter2tp(space, 'P') - return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) + return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval) def to_memory(self, space, w_obj, w_value, offset): address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) @@ -491,7 +490,7 @@ from pypy.module._cppyy.interp_cppyy import W_CPPInstance if isinstance(w_obj, W_CPPInstance): from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_RVALUE - if w_obj.flags & INSTANCE_FLAGS_IS_RVALUE: + if w_obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE: # reject moves as all are explicit raise ValueError("lvalue expected") if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl): @@ -523,14 +522,14 @@ from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE obj = space.interp_w(W_CPPInstance, w_obj) if obj: - if obj.flags & INSTANCE_FLAGS_IS_RVALUE: - obj.flags &= ~INSTANCE_FLAGS_IS_RVALUE + if obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE: + obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE try: return InstanceRefConverter._unwrap_object(self, space, w_obj) except Exception: # TODO: if the method fails on some other converter, then the next # overload can not be an rvalue anymore - obj.flags |= INSTANCE_FLAGS_IS_RVALUE + obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE raise raise oefmt(space.w_ValueError, "object is not an rvalue") @@ -566,10 +565,6 @@ from pypy.module._cppyy import interp_cppyy 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)) - address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value)) - class InstancePtrPtrConverter(InstancePtrConverter): typecode = 'o' @@ -597,6 +592,25 @@ return interp_cppyy.wrap_cppinstance( space, address, self.clsdecl, do_cast=False, is_ref=True) + def to_memory(self, space, w_obj, w_value, offset): + # the actual data member is of object* type, but we receive a pointer to that + # data member in order to modify its value, so by convention, the internal type + # used is object** + address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + cppinstance = space.interp_w(W_CPPInstance, w_value, can_be_None=True) + if cppinstance: + rawobject = cppinstance.get_rawobject() + offset = capi.c_base_offset(space, cppinstance.clsdecl, self.clsdecl, rawobject, 1) + obj_address = capi.direct_ptradd(rawobject, offset) + address[0] = rffi.cast(rffi.VOIDP, obj_address); + # register the value for potential recycling + from pypy.module._cppyy.interp_cppyy import memory_regulator + memory_regulator.register(cppinstance) + else: + raise oefmt(space.w_TypeError, + "cannot pass %T instance as %s", w_value, self.clsdecl.name) + def finalize_call(self, space, w_obj): if self.ref_buffer: set_rawobject(space, w_obj, self.ref_buffer[0]) @@ -607,7 +621,6 @@ self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) class StdStringConverter(InstanceConverter): - def __init__(self, space, extra): from pypy.module._cppyy import interp_cppyy cppclass = interp_cppyy.scope_byname(space, capi.std_string_name) @@ -633,6 +646,34 @@ def free_argument(self, space, arg): capi.c_destruct(space, self.clsdecl, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0])) +class StdStringMoveConverter(StdStringConverter): + def _unwrap_object(self, space, w_obj): + # moving is same as by-ref, but have to check that move is allowed + moveit_reason = 3 + from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE + try: + obj = space.interp_w(W_CPPInstance, w_obj) + if obj and obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE: + obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE + moveit_reason = 1 + else: + moveit_reason = 0 + except: + pass + + if moveit_reason: + try: + return StdStringConverter._unwrap_object(self, space, w_obj) + except Exception: + if moveit_reason == 1: + # TODO: if the method fails on some other converter, then the next + # overload can not be an rvalue anymore + obj = space.interp_w(W_CPPInstance, w_obj) + obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE + raise + + raise oefmt(space.w_ValueError, "object is not an rvalue") + class StdStringRefConverter(InstancePtrConverter): _immutable_fields_ = ['cppclass', 'typecode'] typecode = 'V' @@ -885,6 +926,7 @@ _converters["std::basic_string<char>"] = StdStringConverter _converters["const std::basic_string<char>&"] = StdStringConverter # TODO: shouldn't copy _converters["std::basic_string<char>&"] = StdStringRefConverter +_converters["std::basic_string<char>&&"] = StdStringMoveConverter _converters["PyObject*"] = PyObjectConverter @@ -1002,6 +1044,7 @@ ("std::basic_string<char>", "string"), ("const std::basic_string<char>&", "const string&"), ("std::basic_string<char>&", "string&"), + ("std::basic_string<char>&&", "string&&"), ("PyObject*", "_object*"), ) 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 @@ -1,14 +1,10 @@ import sys from pypy.interpreter.error import oefmt - from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import jit_libffi - from pypy.module._rawffi.interp_rawffi import letter2tp -from pypy.module._rawffi.array import W_Array, W_ArrayInstance - -from pypy.module._cppyy import helper, capi, ffitypes +from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews # Executor objects are used to dispatch C++ methods. They are defined by their # return type only: arguments are converted by Converter objects, and Executors @@ -60,7 +56,7 @@ from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) shape = letter2tp(space, self.typecode) - return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) + return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval) class VoidExecutor(Executor): @@ -98,10 +94,10 @@ def __init__(self, space, extra): Executor.__init__(self, space, extra) self.do_assign = False - self.item = rffi.cast(self.c_type, 0) + self.w_item = space.w_None def set_item(self, space, w_item): - self.item = self._unwrap_object(space, w_item) + self.w_item = w_item self.do_assign = True #def _wrap_object(self, space, obj): @@ -109,7 +105,7 @@ def _wrap_reference(self, space, rffiptr): if self.do_assign: - rffiptr[0] = self.item + rffiptr[0] = rffi.cast(self.c_type, self._unwrap_object(space, self.w_item)) self.do_assign = False return self._wrap_object(space, rffiptr[0]) # all paths, for rtyper 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 @@ -41,6 +41,7 @@ 'void**' : 100, 'float' : 30, 'double' : 10, + 'bool' : 1, 'const string&' : 1, } # solves a specific string ctor overload from rpython.rlib.listsort import make_timsort_class @@ -167,6 +168,7 @@ # # W_CPPOverload: instance methods (base class) # W_CPPConstructorOverload: constructors +# W_CPPAbstractCtorOverload: to provent instantiation of abstract classes # W_CPPStaticOverload: free and static functions # W_CPPTemplateOverload: templated methods # W_CPPTemplateStaticOverload: templated free and static functions @@ -258,7 +260,8 @@ jit.promote(self) cif_descr = self.cif_descr # add extra space for const-ref support (see converter.py) - buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw') + buffer = lltype.malloc(rffi.CCHARP.TO, + cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw') thisoff = 0 try: if cppthis: @@ -412,8 +415,10 @@ def priority(self): total_arg_priority = 0 - for p in [priority.get(arg_type, 0) for arg_type, arg_dflt in self.arg_defs]: - total_arg_priority += p + for arg_type, arg_dflt in self.arg_defs: + total_arg_priority += priority.get(arg_type, 0) + if '&&' in arg_type: + total_arg_priority += 100 return total_arg_priority @rgc.must_be_light_finalizer @@ -433,7 +438,7 @@ class CPPSetItem(CPPMethod): """Method dispatcher specific to Python's __setitem__ mapped onto C++'s - operator[](int). The former function takes an extra argument to assign to + operator[](T). The former function takes an extra argument to assign to the return type of the latter.""" _attrs_ = [] @@ -502,6 +507,9 @@ def descr_get(self, w_obj, w_cls=None): """functionobject.__get__(obj[, type]) -> method""" # TODO: check validity of w_cls if given + # TODO: this does not work for Python 3, which does not have + # unbound methods (probably no common code possible, see also + # pypy/interpreter/function.py) space = self.space asking_for_bound = (space.is_none(w_cls) or not space.is_w(w_obj, space.w_None) or @@ -581,6 +589,16 @@ sig += '\n'+self.functions[i].prototype() return self.space.newtext(sig) + @unwrap_spec(signature='text') + def mp_overload(self, signature): + sig = '(%s)' % signature + for f in self.functions: + if f.signature(False) == sig: + if isinstance(self, W_CPPStaticOverload): + return W_CPPStaticOverload(self.space, self.scope, [f]) + return W_CPPOverload(self.space, self.scope, [f]) + raise oefmt(self.space.w_LookupError, "signature '%s' not found", signature) + # allow user to determine ffi use rules per overload def fget_useffi(self, space): return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI)) @@ -604,10 +622,11 @@ W_CPPOverload.typedef = TypeDef( 'CPPOverload', - __get__ = interp2app(W_CPPOverload.descr_get), - __call__ = interp2app(W_CPPOverload.call_args), - __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPOverload.fget_doc) + __get__ = interp2app(W_CPPOverload.descr_get), + __call__ = interp2app(W_CPPOverload.call_args), + __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), + __overload__ = interp2app(W_CPPOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPOverload.fget_doc) ) @@ -629,21 +648,18 @@ @unwrap_spec(args_w='args_w') def call_args(self, args_w): jit.promote(self) - #if isinstance(args_w[0], W_CPPInstance): - # free function used as bound method, leave in place return self.call_impl(capi.C_NULL_OBJECT, args_w) - # free functions are implemented as methods of 'namespace' classes, remove 'instance' - #return self.call_impl(capi.C_NULL_OBJECT, args_w[1:]) def __repr__(self): return "W_CPPStaticOverload(%s)" % [f.prototype() for f in self.functions] W_CPPStaticOverload.typedef = TypeDef( 'CPPStaticOverload', - __get__ = interp2app(W_CPPStaticOverload.descr_get), - __call__ = interp2app(W_CPPStaticOverload.call_args), - __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) + __get__ = interp2app(W_CPPStaticOverload.descr_get), + __call__ = interp2app(W_CPPStaticOverload.call_args), + __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), + __overload__ = interp2app(W_CPPStaticOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) ) @@ -657,11 +673,6 @@ @unwrap_spec(args_w='args_w') def call_args(self, args_w): jit.promote(self) - # 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) cppinstance = self.space.interp_w(W_CPPInstance, args_w[0]) w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle), args_w[1:]) newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result)) @@ -674,9 +685,27 @@ W_CPPConstructorOverload.typedef = TypeDef( 'CPPConstructorOverload', - __get__ = interp2app(W_CPPConstructorOverload.descr_get), - __call__ = interp2app(W_CPPConstructorOverload.call_args), - __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc) + __get__ = interp2app(W_CPPConstructorOverload.descr_get), + __call__ = interp2app(W_CPPConstructorOverload.call_args), + __overload__ = interp2app(W_CPPConstructorOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc) +) + +class W_CPPAbstractCtorOverload(W_CPPOverload): + _attrs_ = [] + + @unwrap_spec(args_w='args_w') + def call_args(self, args_w): + raise oefmt(self.space.w_TypeError, + "cannot instantiate abstract class '%s'", self.scope.name) + + def __repr__(self): + return "W_CPPAbstractCtorOverload" + +W_CPPAbstractCtorOverload.typedef = TypeDef( + 'CPPAbstractCtorOverload', + __get__ = interp2app(W_CPPAbstractCtorOverload.descr_get), + __call__ = interp2app(W_CPPAbstractCtorOverload.call_args), ) @@ -699,7 +728,7 @@ if args_w: # try to specialize the type match for the given object cppinstance = self.space.interp_w(W_CPPInstance, args_w[i]) - if cppinstance.flags & INSTANCE_FLAGS_IS_RVALUE: + if cppinstance.rt_flags & INSTANCE_FLAGS_IS_RVALUE: sugar = "&&" elif cppinstance.flags & INSTANCE_FLAGS_IS_REF: sugar = "*" @@ -839,11 +868,11 @@ W_CPPTemplateOverload.typedef = TypeDef( 'CPPTemplateOverload', - __get__ = interp2app(W_CPPTemplateOverload.descr_get), - __getitem__ = interp2app(W_CPPTemplateOverload.getitem), - __call__ = interp2app(W_CPPTemplateOverload.call_args), - __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) + __get__ = interp2app(W_CPPTemplateOverload.descr_get), + __getitem__ = interp2app(W_CPPTemplateOverload.getitem), + __call__ = interp2app(W_CPPTemplateOverload.call_args), + __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), + __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) ) class W_CPPTemplateStaticOverload(W_CPPStaticOverload, TemplateOverloadMixin): @@ -897,11 +926,11 @@ W_CPPTemplateStaticOverload.typedef = TypeDef( 'CPPTemplateStaticOverload', - __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), - __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), - __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), - __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc) + __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), + __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), + __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), + __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi), + __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc) ) @@ -929,6 +958,7 @@ def _get_offset(self, cppinstance): if cppinstance: + assert isinstance(cppinstance.clsdecl, W_CPPClassDecl) assert lltype.typeOf(cppinstance.clsdecl.handle) == lltype.typeOf(self.scope.handle) offset = self.offset + cppinstance.clsdecl.get_base_offset(cppinstance, self.scope) else: @@ -1074,6 +1104,8 @@ sig = '(%s)' % signature for f in overload.functions: if f.signature(False) == sig: + if isinstance(overload, W_CPPStaticOverload): + return W_CPPStaticOverload(self.space, self, [f]) return W_CPPOverload(self.space, self, [f]) raise oefmt(self.space.w_LookupError, "no overload matches signature") @@ -1169,9 +1201,13 @@ class W_CPPClassDecl(W_CPPScopeDecl): - _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers'] + _attrs_ = ['space', 'handle', 'name', 'overloads', 'datamembers', 'cppobjects'] _immutable_fields_ = ['handle', 'name', 'overloads[*]', 'datamembers[*]'] + def __init__(self, space, opaque_handle, final_scoped_name): + W_CPPScopeDecl.__init__(self, space, opaque_handle, final_scoped_name) + self.cppobjects = rweakref.RWeakValueDictionary(int, W_CPPInstance) + def _build_overloads(self): assert len(self.overloads) == 0 methods_tmp = {}; ftype_tmp = {} @@ -1210,7 +1246,10 @@ ftype = ftype_tmp[pyname] CPPMethodSort(methods).sort() if ftype & FUNCTION_IS_CONSTRUCTOR: - overload = W_CPPConstructorOverload(self.space, self, methods[:]) + if capi.c_is_abstract(self.space, self.handle): + overload = W_CPPAbstractCtorOverload(self.space, self, methods[:]) + else: + overload = W_CPPConstructorOverload(self.space, self, methods[:]) elif ftype & FUNCTION_IS_STATIC: if ftype & FUNCTION_IS_TEMPLATE: cppname = capi.c_method_name(self.space, methods[0].cppmethod) @@ -1276,10 +1315,12 @@ raise self.missing_attribute_error(name) def get_base_offset(self, cppinstance, calling_scope): + assert isinstance(cppinstance.clsdecl, W_CPPClassDecl) assert self == cppinstance.clsdecl return 0 def get_cppthis(self, cppinstance, calling_scope): + assert isinstance(cppinstance.clsdecl, W_CPPClassDecl) assert self == cppinstance.clsdecl return cppinstance.get_rawobject() @@ -1315,12 +1356,14 @@ class W_CPPComplexClassDecl(W_CPPClassDecl): def get_base_offset(self, cppinstance, calling_scope): + assert isinstance(cppinstance.clsdecl, W_CPPComplexClassDecl) assert self == cppinstance.clsdecl offset = capi.c_base_offset(self.space, - self, calling_scope, cppinstance.get_rawobject(), 1) + self, calling_scope, cppinstance.get_rawobject(), 1) return offset def get_cppthis(self, cppinstance, calling_scope): + assert isinstance(cppinstance.clsdecl, W_CPPComplexClassDecl) assert self == cppinstance.clsdecl offset = self.get_base_offset(cppinstance, calling_scope) return capi.direct_ptradd(cppinstance.get_rawobject(), offset) @@ -1340,9 +1383,9 @@ class W_CPPInstance(W_Root): - _attrs_ = ['space', 'clsdecl', '_rawobject', 'smartdecl', 'deref', 'flags', + _attrs_ = ['space', 'clsdecl', '_rawobject', 'smartdecl', 'deref', 'flags', 'rt_flags', 'finalizer_registered'] - _immutable_fields_ = ['clsdecl', 'smartdecl', 'deref'] + _immutable_fields_ = ['clsdecl', 'smartdecl', 'deref', 'flags'] finalizer_registered = False @@ -1350,6 +1393,7 @@ smartdecl=None, deref=rffi.cast(capi.C_METHOD, 0)): self.space = space self.clsdecl = decl + assert isinstance(self.clsdecl, W_CPPClassDecl) assert lltype.typeOf(rawobject) == capi.C_OBJECT assert not isref or rawobject self._rawobject = rawobject @@ -1357,15 +1401,16 @@ self.flags = 0 if isref or (smartdecl and deref): self.flags |= INSTANCE_FLAGS_IS_REF + self.rt_flags = 0 if python_owns: - self.flags |= INSTANCE_FLAGS_PYTHON_OWNS + self.rt_flags |= INSTANCE_FLAGS_PYTHON_OWNS self._opt_register_finalizer() self.smartdecl = smartdecl self.deref = deref def _opt_register_finalizer(self): if not self.finalizer_registered and not hasattr(self.space, "fake"): - assert self.flags & INSTANCE_FLAGS_PYTHON_OWNS + assert self.rt_flags & INSTANCE_FLAGS_PYTHON_OWNS self.register_finalizer(self.space) self.finalizer_registered = True @@ -1377,17 +1422,18 @@ # allow user to determine ownership rules on a per object level def fget_python_owns(self, space): - return space.newbool(bool(self.flags & INSTANCE_FLAGS_PYTHON_OWNS)) + return space.newbool(bool(self.rt_flags & INSTANCE_FLAGS_PYTHON_OWNS)) @unwrap_spec(value=bool) def fset_python_owns(self, space, value): if space.is_true(value): - self.flags |= INSTANCE_FLAGS_PYTHON_OWNS + self.rt_flags |= INSTANCE_FLAGS_PYTHON_OWNS self._opt_register_finalizer() else: - self.flags &= ~INSTANCE_FLAGS_PYTHON_OWNS + self.rt_flags &= ~INSTANCE_FLAGS_PYTHON_OWNS def get_cppthis(self, calling_scope): + assert isinstance(self.clsdecl, W_CPPClassDecl) return self.clsdecl.get_cppthis(self, calling_scope) def get_rawobject(self): @@ -1494,6 +1540,7 @@ def destruct(self): if self._rawobject: + assert isinstance(self.clsdecl, W_CPPClassDecl) if self.smartdecl and self.deref: klass = self.smartdecl elif not (self.flags & INSTANCE_FLAGS_IS_REF): @@ -1505,7 +1552,7 @@ self._rawobject = capi.C_NULL_OBJECT def _finalize_(self): - if self.flags & INSTANCE_FLAGS_PYTHON_OWNS: + if self.rt_flags & INSTANCE_FLAGS_PYTHON_OWNS: self.destruct() W_CPPInstance.typedef = TypeDef( @@ -1527,31 +1574,33 @@ class MemoryRegulator: - # TODO: (?) An object address is not unique if e.g. the class has a - # public data member of class type at the start of its definition and - # has no virtual functions. A _key class that hashes on address and - # type would be better, but my attempt failed in the rtyper, claiming - # a call on None ("None()") and needed a default ctor. (??) - # Note that for now, the associated test carries an m_padding to make - # a difference in the addresses. - def __init__(self): - self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance) + _immutable_ = True - def register(self, obj): + @staticmethod + def register(obj): if not obj._rawobject: return - int_address = int(rffi.cast(rffi.LONG, obj._rawobject)) - self.objects.set(int_address, obj) + addr_as_int = int(rffi.cast(rffi.LONG, obj.get_rawobject())) + clsdecl = obj.clsdecl + assert isinstance(clsdecl, W_CPPClassDecl) + clsdecl.cppobjects.set(addr_as_int, obj) - def unregister(self, obj): + @staticmethod + def unregister(obj): if not obj._rawobject: return - int_address = int(rffi.cast(rffi.LONG, obj._rawobject)) - self.objects.set(int_address, None) + addr_as_int = int(rffi.cast(rffi.LONG, obj.get_rawobject())) + clsdecl = obj.clsdecl + assert isinstance(clsdecl, W_CPPClassDecl) + clsdecl.cppobjects.set(addr_as_int, None) # actually deletes (pops) - def retrieve(self, address): - int_address = int(rffi.cast(rffi.LONG, address)) - return self.objects.get(int_address) + @staticmethod + def retrieve(clsdecl, address): + if not address: + return None + addr_as_int = int(rffi.cast(rffi.LONG, address)) + assert isinstance(clsdecl, W_CPPClassDecl) + return clsdecl.cppobjects.get(addr_as_int) memory_regulator = MemoryRegulator() @@ -1597,8 +1646,11 @@ # 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.clsdecl is clsdecl: + address = rawobject + if is_ref: + address = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, address)[0]) + obj = memory_regulator.retrieve(clsdecl, address) + if obj is not None: return obj # fresh creation @@ -1649,7 +1701,7 @@ """Casts the given instance into an C++-style rvalue.""" obj = space.interp_w(W_CPPInstance, w_obj) if obj: - obj.flags |= INSTANCE_FLAGS_IS_RVALUE + obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE return w_obj diff --git a/pypy/module/_cppyy/lowlevelviews.py b/pypy/module/_cppyy/lowlevelviews.py new file mode 100644 --- /dev/null +++ b/pypy/module/_cppyy/lowlevelviews.py @@ -0,0 +1,54 @@ +# Naked C++ pointers carry no useful size or layout information, but often +# such information is externnally available. Low level views are arrays with +# a few more methods allowing such information to be set. Afterwards, it is +# simple to pass these views on to e.g. numpy (w/o the need to copy). + +from pypy.interpreter.error import oefmt +from pypy.interpreter.gateway import interp2app, unwrap_spec +from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty_w +from pypy.module._rawffi.array import W_ArrayInstance + + +class W_LowLevelView(W_ArrayInstance): + def __init__(self, space, shape, length, address): + assert address # if not address, base class will allocate memory + W_ArrayInstance.__init__(self, space, shape, length, address) + + @unwrap_spec(args_w='args_w') + def reshape(self, space, args_w): + # llviews are only created from non-zero addresses, so we only need + # to adjust length and shape + + nargs = len(args_w) + if nargs == 0: + raise oefmt(space.w_TypeError, "reshape expects a tuple argument") + + newshape_w = args_w + if nargs != 1 or not space.isinstance_w(args_w[0], space.w_tuple) or \ + not space.len_w(args_w[0]) == 1: + raise oefmt(space.w_TypeError, + "tuple object of length 1 expected, received %T", args_w[0]) + + w_shape = args_w[0] + + # shape in W_ArrayInstance-speak is somewhat different from what + # e.g. numpy thinks of it: self.shape contains the info (itemcode, + # size, etc.) of a single entry; length is user-facing shape + self.length = space.int_w(space.getitem(w_shape, space.newint(0))) + + +W_LowLevelView.typedef = TypeDef( + 'LowLevelView', + __repr__ = interp2app(W_LowLevelView.descr_repr), + __setitem__ = interp2app(W_LowLevelView.descr_setitem), + __getitem__ = interp2app(W_LowLevelView.descr_getitem), + __len__ = interp2app(W_LowLevelView.getlength), + buffer = GetSetProperty(W_LowLevelView.getbuffer), + shape = interp_attrproperty_w('shape', W_LowLevelView), + free = interp2app(W_LowLevelView.free), + byptr = interp2app(W_LowLevelView.byptr), + itemaddress = interp2app(W_LowLevelView.descr_itemaddress), + reshape = interp2app(W_LowLevelView.reshape), +) +W_ArrayInstance.typedef.acceptable_as_base_class = False + 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 @@ -73,7 +73,8 @@ # C++ namespace base class (the C++ class base class defined in _post_import_startup) class CPPNamespace(with_metaclass(CPPNamespaceMeta, object)): - pass + def __init__(self): + raise TypeError("cannot instantiate namespace '%s'", self.__cppname__) # TODO: this can be moved to the interp level (and share template argument @@ -450,6 +451,14 @@ return self.__real_init__(*args) pyclass.__init__ = vector_init + # size-up the return of data() + pyclass.__real_data = pyclass.data + def data_with_len(self): + arr = self.__real_data() + arr.reshape((len(self),)) + return arr + pyclass.data = data_with_len + # combine __getitem__ and __len__ to make a pythonized __getitem__ if '__getitem__' in pyclass.__dict__ and '__len__' in pyclass.__dict__: pyclass._getitem__unchecked = pyclass.__getitem__ diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -4,20 +4,32 @@ // for testing of default arguments -#define IMPLEMENT_DEFAULTER_CLASS(type, tname) \ +#define IMPLEMENT_DEFAULTERS(type, tname) \ tname##_defaulter::tname##_defaulter(type a, type b, type c) { \ - m_a = a; m_b = b; m_c = c; \ + m_a = a; m_b = b; m_c = c; \ +} \ +type tname##_defaulter_func(int idx, type a, type b, type c) { \ + if (idx == 0) return a; \ + if (idx == 1) return b; \ + if (idx == 2) return c; \ + return (type)idx; \ } -IMPLEMENT_DEFAULTER_CLASS(short, short) -IMPLEMENT_DEFAULTER_CLASS(unsigned short, ushort) -IMPLEMENT_DEFAULTER_CLASS(int, int) -IMPLEMENT_DEFAULTER_CLASS(unsigned, uint) -IMPLEMENT_DEFAULTER_CLASS(long, long) -IMPLEMENT_DEFAULTER_CLASS(unsigned long, ulong) -IMPLEMENT_DEFAULTER_CLASS(long long, llong) -IMPLEMENT_DEFAULTER_CLASS(unsigned long long, ullong) -IMPLEMENT_DEFAULTER_CLASS(float, float) -IMPLEMENT_DEFAULTER_CLASS(double, double) +IMPLEMENT_DEFAULTERS(short, short) +IMPLEMENT_DEFAULTERS(unsigned short, ushort) +IMPLEMENT_DEFAULTERS(int, int) +IMPLEMENT_DEFAULTERS(unsigned, uint) +IMPLEMENT_DEFAULTERS(long, long) +IMPLEMENT_DEFAULTERS(unsigned long, ulong) +IMPLEMENT_DEFAULTERS(long long, llong) +IMPLEMENT_DEFAULTERS(unsigned long long, ullong) +IMPLEMENT_DEFAULTERS(float, float) +IMPLEMENT_DEFAULTERS(double, double) + +std::string string_defaulter_func(int idx, const std::string& name1, std::string name2) { + if (idx == 0) return name1; + if (idx == 1) return name2; + return "mies"; +} // for esoteric inheritance testing @@ -77,11 +89,11 @@ double my_global_array[500]; static double sd = 1234.; double* my_global_ptr = &sd; +const char my_global_string2[] = "zus jet teun"; some_int_holder my_global_int_holders[5] = { some_int_holder(13), some_int_holder(42), some_int_holder(88), some_int_holder(-1), some_int_holder(17) }; - // for life-line and identity testing int some_class_with_data::some_data::s_num_data = 0; diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -4,24 +4,27 @@ //=========================================================================== -#define DECLARE_DEFAULTER_CLASS(type, tname) \ +#define DECLARE_DEFAULTERS(type, tname) \ class tname##_defaulter { \ public: \ tname##_defaulter(type a = 11, type b = 22, type c = 33); \ \ public: \ type m_a, m_b, m_c; \ -}; -DECLARE_DEFAULTER_CLASS(short, short) // for testing of default arguments -DECLARE_DEFAULTER_CLASS(unsigned short, ushort) -DECLARE_DEFAULTER_CLASS(int, int) -DECLARE_DEFAULTER_CLASS(unsigned, uint) -DECLARE_DEFAULTER_CLASS(long, long) -DECLARE_DEFAULTER_CLASS(unsigned long, ulong) -DECLARE_DEFAULTER_CLASS(long long, llong) -DECLARE_DEFAULTER_CLASS(unsigned long long, ullong) -DECLARE_DEFAULTER_CLASS(float, float) -DECLARE_DEFAULTER_CLASS(double, double) +}; \ +type tname##_defaulter_func(int idx = 0, type a = 11, type b = 22, type c = 33); +DECLARE_DEFAULTERS(short, short) // for testing of default arguments +DECLARE_DEFAULTERS(unsigned short, ushort) +DECLARE_DEFAULTERS(int, int) +DECLARE_DEFAULTERS(unsigned, uint) +DECLARE_DEFAULTERS(long, long) +DECLARE_DEFAULTERS(unsigned long, ulong) +DECLARE_DEFAULTERS(long long, llong) +DECLARE_DEFAULTERS(unsigned long long, ullong) +DECLARE_DEFAULTERS(float, float) +DECLARE_DEFAULTERS(double, double) + +std::string string_defaulter_func(int idx, const std::string& name1 = "aap", std::string name2 = "noot"); //=========================================================================== @@ -274,7 +277,8 @@ extern double my_global_double; // a couple of globals for access testing extern double my_global_array[500]; extern double* my_global_ptr; -static const char my_global_string[] = "aap " " noot " " mies"; +static const char my_global_string1[] = "aap " " noot " " mies"; +extern const char my_global_string2[]; class some_int_holder { public: @@ -311,6 +315,11 @@ some_data m_data; }; +class refers_to_self { // for data member reuse testing +public: + refers_to_self* m_other = nullptr; +}; + //=========================================================================== class pointer_pass { // for testing passing of void*'s @@ -419,3 +428,34 @@ void throw_anything(); void throw_exception(); }; + + +//=========================================================================== +class UsingBase { // using declaration testing +public: + UsingBase(int n = 13) : m_int(n) {} + virtual char vcheck() { return 'A'; } + int m_int; +}; + +class UsingDerived : public UsingBase { +public: + using UsingBase::UsingBase; + virtual char vcheck() { return 'B'; } + int m_int2 = 42; +}; + + +//=========================================================================== +class TypedefToPrivateClass { // typedef resolution testing +private: + class PC { + public: + PC(int i) : m_val(i) {} + int m_val; + }; + +public: + typedef PC PP; + PP f() { return PC(42); } +}; diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -1,6 +1,7 @@ <lcgdict> <class pattern="*_defaulter" /> + <function pattern="*_defaulter_func" /> <class name="base_class" /> <class name="derived_class" /> @@ -32,6 +33,7 @@ <class name="some_convertible" /> <class name="some_class_with_data" /> <class name="some_class_with_data::some_data" /> + <class name="refers_to_self" /> <class name="some_comparable" /> <function name="operator==" /> @@ -59,5 +61,7 @@ <class name="overload_the_other_way" /> <class name="Thrower" /> + <class pattern="Using*" /> + <class name="TypedefToPrivateClass" /> </lcgdict> diff --git a/pypy/module/_cppyy/test/stltypes.cxx b/pypy/module/_cppyy/test/stltypes.cxx --- a/pypy/module/_cppyy/test/stltypes.cxx +++ b/pypy/module/_cppyy/test/stltypes.cxx @@ -1,6 +1,20 @@ #include "stltypes.h" +//- explicit instantiations of used comparisons +#if defined __clang__ +namespace std { +#define ns_prefix std:: +#elif defined(__GNUC__) || defined(__GNUG__) +namespace __gnu_cxx { +#define ns_prefix +#endif +template bool ns_prefix operator==(const std::vector<int>::iterator&, + const std::vector<int>::iterator&); +template bool ns_prefix operator!=(const std::vector<int>::iterator&, + const std::vector<int>::iterator&); +} + //- class with lots of std::string handling stringy_class::stringy_class(const char* s) : m_string(s) {} @@ -9,3 +23,33 @@ void stringy_class::set_string1(const std::string& s) { m_string = s; } void stringy_class::set_string2(std::string s) { m_string = s; } + + +//- helpers for testing array +int ArrayTest::get_pp_px(Point** p, int idx) { + return p[idx]->px; +} + +int ArrayTest::get_pp_py(Point** p, int idx) { + return p[idx]->py; +} + +int ArrayTest::get_pa_px(Point* p[], int idx) { + return p[idx]->px; +} + +int ArrayTest::get_pa_py(Point* p[], int idx) { + return p[idx]->py; +} + + +// helpers for string testing +std::string str_array_1[3] = {"a", "b", "c"}; +std::string str_array_2[] = {"d", "e", "f", "g"}; +std::string str_array_3[3][2] = {{"a", "b"}, {"c", "d"}, {"e", "f"}}; +std::string str_array_4[4][2][2] = { + {{"a", "b"}, {"c", "d"}}, + {{"e", "f"}, {"g", "h"}}, + {{"i", "j"}, {"k", "l"}}, + {{"m", "n"}, {"o", "p"}}, +}; diff --git a/pypy/module/_cppyy/test/stltypes.h b/pypy/module/_cppyy/test/stltypes.h --- a/pypy/module/_cppyy/test/stltypes.h +++ b/pypy/module/_cppyy/test/stltypes.h @@ -37,3 +37,49 @@ std::string operator[](double) { return "double"; } std::string operator[](const std::string&) { return "string"; } }; + + +//- instantiations of used STL types +namespace { + + stl_like_class<int> stlc_1; + +} // unnamed namespace + + +// comps for int only to allow testing: normal use of vector is looping over a +// range-checked version of __getitem__ +#if defined(__clang__) && defined(__APPLE__) +namespace std { +#define ns_prefix std:: +#elif defined(__GNUC__) || defined(__GNUG__) +namespace __gnu_cxx { +#define ns_prefix +#endif +extern template bool ns_prefix operator==(const std::vector<int>::iterator&, + const std::vector<int>::iterator&); +extern template bool ns_prefix operator!=(const std::vector<int>::iterator&, + const std::vector<int>::iterator&); +} + + +//- helpers for testing array +namespace ArrayTest { + +struct Point { + int px, py; +}; + +int get_pp_px(Point** p, int idx); +int get_pp_py(Point** p, int idx); +int get_pa_px(Point* p[], int idx); +int get_pa_py(Point* p[], int idx); + +} // namespace ArrayTest + + +// helpers for string testing +extern std::string str_array_1[3]; +extern std::string str_array_2[]; +extern std::string str_array_3[3][2]; +extern std::string str_array_4[4][2][2]; diff --git a/pypy/module/_cppyy/test/stltypes.xml b/pypy/module/_cppyy/test/stltypes.xml --- a/pypy/module/_cppyy/test/stltypes.xml +++ b/pypy/module/_cppyy/test/stltypes.xml @@ -1,10 +1,13 @@ <lcgdict> - <namespace name="std" /> + <class name="just_a_class" /> + <class name="stringy_class" /> + <class pattern="stl_like_class<*>" /> - <class name="just_a_class" /> + <namespace name="ArrayTest" /> + <class pattern="ArrayTest::*" /> + <function pattern="ArrayTest::*" /> - <class name="std::string" /> - <class name="stringy_class" /> + <variable pattern="str_array_*" /> </lcgdict> diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py b/pypy/module/_cppyy/test/test_advancedcpp.py --- a/pypy/module/_cppyy/test/test_advancedcpp.py +++ b/pypy/module/_cppyy/test/test_advancedcpp.py @@ -56,6 +56,12 @@ assert d.m_b == t(4) assert d.m_c == t(5) d.__destruct__() + + defaulter_func = getattr(cppyy.gbl, '%s_defaulter_func' %n) + answers = [11, 22, 33, 3] + for idx in range(4): + assert defaulter_func(idx) == answers[idx] + test_defaulter('short', int) test_defaulter('ushort', int) test_defaulter('int', int) @@ -67,6 +73,13 @@ test_defaulter('float', float) test_defaulter('double', float) + assert cppyy.gbl.string_defaulter_func(0) == "aap" + assert cppyy.gbl.string_defaulter_func(0, "zus") == "zus" + assert cppyy.gbl.string_defaulter_func(1) == "noot" + assert cppyy.gbl.string_defaulter_func(1, "zus") == "noot" + assert cppyy.gbl.string_defaulter_func(1, "zus", "jet") == "jet" + assert cppyy.gbl.string_defaulter_func(2) == "mies" + def test02_simple_inheritance(self): """Test binding of a basic inheritance structure""" @@ -148,6 +161,8 @@ assert gbl.a_ns.d_ns.e_class.f_class.s_f == 66 assert gbl.a_ns.d_ns.e_class.f_class().m_f == -6 + raises(TypeError, gbl.a_ns) + def test03a_namespace_lookup_on_update(self): """Test whether namespaces can be shared across dictionaries.""" @@ -474,6 +489,23 @@ d2.__destruct__() d1.__destruct__() + RTS = cppyy.gbl.refers_to_self + + r1 = RTS() + r2 = RTS() + r1.m_other = r2 + + r3 = r1.m_other + r4 = r1.m_other + assert r3 is r4 + + assert r3 == r2 + assert r3 is r2 + + r3.extra = 42 + assert r2.extra == 42 + assert r4.extra == 42 + def test11_multi_methods(self): """Test calling of methods from multiple inheritance""" @@ -653,10 +685,18 @@ assert cppyy.gbl.my_global_double == 12. assert len(cppyy.gbl.my_global_array) == 500 - assert cppyy.gbl.my_global_string == "aap noot mies" + assert cppyy.gbl.my_global_string1 == "aap noot mies" + return # next line currently crashes + assert cppyy.gbl.my_global_string2 == "zus jet teun" # TODO: currently fails b/c double** not understood as &double* #assert cppyy.gbl.my_global_ptr[0] == 1234. + v = cppyy.gbl.my_global_int_holders + assert len(v) == 5 + expected_vals = [13, 42, 88, -1, 17] + for i in range(len(v)): + assert v[i].m_val == expected_vals[i] + def test22_exceptions(self): """Catching of C++ exceptions""" @@ -675,3 +715,35 @@ t.throw_exception() except Exception as e: "C++ function failed" in str(e) + + def test23_using(self): + """Accessibility of using declarations""" + + import _cppyy as cppyy + + assert cppyy.gbl.UsingBase().vcheck() == 'A' + + B = cppyy.gbl.UsingDerived + assert not 'UsingBase' in B.__init__.__doc__ + + b1 = B() + assert b1.m_int == 13 + assert b1.m_int2 == 42 + assert b1.vcheck() == 'B' + + b2 = B(10) + assert b2.m_int == 10 + assert b2.m_int2 == 42 + assert b2.vcheck() == 'B' + + b3 = B(b2) + assert b3.m_int == 10 + assert b3.m_int2 == 42 + assert b3.vcheck() == 'B' + + def test24_typedef_to_private_class(self): + """Typedefs to private classes should not resolve""" + + import _cppyy as cppyy + + assert cppyy.gbl.TypedefToPrivateClass().f().m_val == 42 diff --git a/pypy/module/_cppyy/test/test_datatypes.py b/pypy/module/_cppyy/test/test_datatypes.py --- a/pypy/module/_cppyy/test/test_datatypes.py +++ b/pypy/module/_cppyy/test/test_datatypes.py @@ -183,7 +183,7 @@ names = ['uchar', 'short', 'ushort', 'int', 'uint', 'long', 'ulong'] import array a = range(self.N) - atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L' ] + atypes = ['B', 'h', 'H', 'i', 'I', 'l', 'L'] for j in range(len(names)): b = array.array(atypes[j], a) setattr(c, 'm_'+names[j]+'_array', b) # buffer copies @@ -191,6 +191,7 @@ assert eval('c.m_%s_array[i]' % names[j]) == b[i] setattr(c, 'm_'+names[j]+'_array2', b) # pointer copies + assert 3 < self.N b[3] = 28 for i in range(self.N): assert eval('c.m_%s_array2[i]' % names[j]) == b[i] @@ -212,7 +213,7 @@ a = range(self.N) # test arrays in mixed order, to give overload resolution a workout - for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l' ]: + for t in ['d', 'i', 'f', 'H', 'I', 'h', 'L', 'l']: b = array.array(t, a) # typed passing @@ -691,9 +692,6 @@ 'get_long_array', 'get_long_array2', 'get_ulong_array', 'get_ulong_array2']: arr = getattr(c, func)() - arr = arr.shape.fromaddress(arr.itemaddress(0), self.N) - - """TODO: interface change ... arr.reshape((self.N,)) assert len(arr) == self.N @@ -705,7 +703,7 @@ l = list(arr) for i in range(self.N): - assert arr[i] == l[i]""" + assert arr[i] == l[i] def test20_voidp(self): """Test usage of void* data""" @@ -758,6 +756,15 @@ def test21_function_pointers(self): """Function pointer passing""" + import os + + # TODO: currently crashes if fast path disabled + try: + if os.environ['CPPYY_DISABLE_FASTPATH']: + return + except KeyError: + pass + import _cppyy as cppyy f1 = cppyy.gbl.sum_of_int diff --git a/pypy/module/_cppyy/test/test_overloads.py b/pypy/module/_cppyy/test/test_overloads.py --- a/pypy/module/_cppyy/test/test_overloads.py +++ b/pypy/module/_cppyy/test/test_overloads.py @@ -58,15 +58,19 @@ c = c_overload() raises(TypeError, c.__dispatch__, 'get_int', 12) raises(LookupError, c.__dispatch__, 'get_int', 'does_not_exist') - assert c.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42 - assert c.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13 + assert c.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42 + assert c_overload.get_int.__overload__('a_overload*')(c, a_overload()) == 42 + assert c.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13 + assert c_overload.get_int.__overload__('b_overload*')(c, b_overload()) == 13 assert c_overload().__dispatch__('get_int', 'a_overload*')(a_overload()) == 42 # TODO: #assert c_overload.__dispatch__('get_int', 'b_overload*')(c, b_overload()) == 13 d = d_overload() - assert d.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42 - assert d.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13 + assert d.__dispatch__('get_int', 'a_overload*')(a_overload()) == 42 + assert d_overload.get_int.__overload__('a_overload*')(d, a_overload()) == 42 + assert d.__dispatch__('get_int', 'b_overload*')(b_overload()) == 13 + assert d_overload.get_int.__overload__('b_overload*')(d, b_overload()) == 13 nb = ns_a_overload.b_overload() raises(TypeError, nb.f, c_overload()) diff --git a/pypy/module/_cppyy/test/test_stltypes.py b/pypy/module/_cppyy/test/test_stltypes.py --- a/pypy/module/_cppyy/test/test_stltypes.py +++ b/pypy/module/_cppyy/test/test_stltypes.py @@ -22,12 +22,12 @@ def test01_builtin_type_vector_types(self): """Test access to std::vector<int>/std::vector<double>""" - import _cppyy + import _cppyy as cppyy - assert _cppyy.gbl.std is _cppyy.gbl.std - assert _cppyy.gbl.std.vector is _cppyy.gbl.std.vector + assert cppyy.gbl.std is cppyy.gbl.std + assert cppyy.gbl.std.vector is cppyy.gbl.std.vector - assert callable(_cppyy.gbl.std.vector) + assert callable(cppyy.gbl.std.vector) type_info = ( ("int", int), @@ -36,14 +36,14 @@ ) for c_type, p_type in type_info: - tv1 = getattr(_cppyy.gbl.std, 'vector<%s>' % c_type) - tv2 = _cppyy.gbl.std.vector(p_type) + tv1 = getattr(cppyy.gbl.std, 'vector<%s>' % c_type) + tv2 = cppyy.gbl.std.vector(p_type) assert tv1 is tv2 - assert tv1.iterator is _cppyy.gbl.std.vector(p_type).iterator + assert tv1.iterator is cppyy.gbl.std.vector(p_type).iterator #----- - v = tv1(); v += range(self.N) # default args from Reflex are useless :/ - if p_type == int: # only type with == and != reflected in .xml + v = tv1(); v += range(self.N) + if p_type == int: assert v.begin().__eq__(v.begin()) assert v.begin() == v.begin() assert v.end() == v.end() @@ -58,6 +58,7 @@ assert v.size() == self.N assert len(v) == self.N + assert len(v.data()) == self.N #----- v = tv1() @@ -73,16 +74,16 @@ def test02_user_type_vector_type(self): """Test access to an std::vector<just_a_class>""" - import _cppyy + import _cppyy as cppyy - assert _cppyy.gbl.std is _cppyy.gbl.std - assert _cppyy.gbl.std.vector is _cppyy.gbl.std.vector + assert cppyy.gbl.std is cppyy.gbl.std + assert cppyy.gbl.std.vector is cppyy.gbl.std.vector - assert callable(_cppyy.gbl.std.vector) + assert callable(cppyy.gbl.std.vector) - tv1 = getattr(_cppyy.gbl.std, 'vector<just_a_class>') - tv2 = _cppyy.gbl.std.vector('just_a_class') - tv3 = _cppyy.gbl.std.vector(_cppyy.gbl.just_a_class) + tv1 = getattr(cppyy.gbl.std, 'vector<just_a_class>') + tv2 = cppyy.gbl.std.vector('just_a_class') + tv3 = cppyy.gbl.std.vector(cppyy.gbl.just_a_class) assert tv1 is tv2 assert tv2 is tv3 @@ -95,7 +96,7 @@ assert hasattr(v, 'end' ) for i in range(self.N): - v.push_back(_cppyy.gbl.just_a_class()) + v.push_back(cppyy.gbl.just_a_class()) v[i].m_i = i assert v[i].m_i == i @@ -105,9 +106,9 @@ def test03_empty_vector_type(self): """Test behavior of empty std::vector<int>""" - import _cppyy + import _cppyy as cppyy - v = _cppyy.gbl.std.vector(int)() + v = cppyy.gbl.std.vector(int)() for arg in v: pass v.__destruct__() @@ -115,9 +116,9 @@ def test04_vector_iteration(self): """Test iteration over an std::vector<int>""" - import _cppyy + import _cppyy as cppyy - v = _cppyy.gbl.std.vector(int)() + v = cppyy.gbl.std.vector(int)() for i in range(self.N): v.push_back(i) @@ -140,9 +141,9 @@ def test05_push_back_iterables_with_iadd(self): """Test usage of += of iterable on push_back-able container""" - import _cppyy + import _cppyy as cppyy - v = _cppyy.gbl.std.vector(int)() + v = cppyy.gbl.std.vector(int)() v += [1, 2, 3] assert len(v) == 3 @@ -159,7 +160,7 @@ raises(TypeError, v.__iadd__, (7, '8')) # string shouldn't pass assert len(v) == 7 # TODO: decide whether this should roll-back - v2 = _cppyy.gbl.std.vector(int)() + v2 = cppyy.gbl.std.vector(int)() v2 += [8, 9] assert len(v2) == 2 assert v2[0] == 8 @@ -174,9 +175,9 @@ def test06_vector_indexing(self): """Test python-style indexing to an std::vector<int>""" - import _cppyy + import _cppyy as cppyy - v = _cppyy.gbl.std.vector(int)() + v = cppyy.gbl.std.vector(int)() for i in range(self.N): v.push_back(i) @@ -209,9 +210,9 @@ def test01_string_argument_passing(self): """Test mapping of python strings and std::string""" - import _cppyy - std = _cppyy.gbl.std - stringy_class = _cppyy.gbl.stringy_class + import _cppyy as cppyy + std = cppyy.gbl.std + stringy_class = cppyy.gbl.stringy_class c, s = stringy_class(""), std.string("test1") @@ -240,9 +241,9 @@ def test02_string_data_access(self): """Test access to std::string object data members""" - import _cppyy - std = _cppyy.gbl.std - stringy_class = _cppyy.gbl.stringy_class + import _cppyy as cppyy + std = cppyy.gbl.std + stringy_class = cppyy.gbl.stringy_class c, s = stringy_class("dummy"), std.string("test string") @@ -261,9 +262,9 @@ return # don't bother; is fixed in cling-support - import _cppyy - std = _cppyy.gbl.std - stringy_class = _cppyy.gbl.stringy_class + import _cppyy as cppyy + std = cppyy.gbl.std + stringy_class = cppyy.gbl.stringy_class t0 = "aap\0noot" assert t0 == "aap\0noot" @@ -288,8 +289,8 @@ def test01_builtin_list_type(self): """Test access to a list<int>""" - import _cppyy - std = _cppyy.gbl.std + import _cppyy as cppyy + std = cppyy.gbl.std type_info = ( ("int", int), @@ -299,9 +300,9 @@ for c_type, p_type in type_info: tl1 = getattr(std, 'list<%s>' % c_type) - tl2 = _cppyy.gbl.std.list(p_type) + tl2 = cppyy.gbl.std.list(p_type) assert tl1 is tl2 - assert tl1.iterator is _cppyy.gbl.std.list(p_type).iterator + assert tl1.iterator is cppyy.gbl.std.list(p_type).iterator #----- a = tl1() @@ -323,8 +324,8 @@ def test02_empty_list_type(self): """Test behavior of empty list<int>""" - import _cppyy - std = _cppyy.gbl.std + import _cppyy as cppyy + std = cppyy.gbl.std a = std.list(int)() for arg in a: @@ -344,8 +345,8 @@ def test01_builtin_map_type(self): """Test access to a map<int,int>""" - import _cppyy - std = _cppyy.gbl.std + import _cppyy as cppyy + std = cppyy.gbl.std a = std.map(int, int)() for i in range(self.N): @@ -373,8 +374,8 @@ def test02_keyed_maptype(self): """Test access to a map<std::string,int>""" - import _cppyy - std = _cppyy.gbl.std + import _cppyy as cppyy + std = cppyy.gbl.std a = std.map(std.string, int)() for i in range(self.N): @@ -386,8 +387,8 @@ def test03_empty_maptype(self): """Test behavior of empty map<int,int>""" - import _cppyy - std = _cppyy.gbl.std + import _cppyy as cppyy + std = cppyy.gbl.std m = std.map(int, int)() for key, value in m: @@ -396,8 +397,9 @@ def test04_unsignedvalue_typemap_types(self): """Test assignability of maps with unsigned value types""" - import _cppyy, math, sys - std = _cppyy.gbl.std + import _cppyy as cppyy + import math, sys + std = cppyy.gbl.std mui = std.map(str, 'unsigned int')() mui['one'] = 1 @@ -420,8 +422,8 @@ def test05_STL_like_class_indexing_overloads(self): """Test overloading of operator[] in STL like class""" - import _cppyy - stl_like_class = _cppyy.gbl.stl_like_class + import _cppyy as cppyy + stl_like_class = cppyy.gbl.stl_like_class a = stl_like_class(int)() assert a["some string" ] == 'string' @@ -430,8 +432,8 @@ def test06_STL_like_class_iterators(self): """Test the iterator protocol mapping for an STL like class""" - import _cppyy - stl_like_class = _cppyy.gbl.stl_like_class + import _cppyy as cppyy + stl_like_class = cppyy.gbl.stl_like_class a = stl_like_class(int)() for i in a: @@ -452,8 +454,8 @@ def test01_builtin_vector_iterators(self): """Test iterator comparison with operator== reflected""" - import _cppyy - std = _cppyy.gbl.std + import _cppyy as cppyy + std = cppyy.gbl.std v = std.vector(int)() v.resize(1) @@ -489,9 +491,9 @@ def test01_explicit_templates(self): """Explicit use of Template class""" - import _cppyy + import _cppyy as cppyy - vector = _cppyy.Template('vector', _cppyy.gbl.std) + vector = cppyy.Template('vector', cppyy.gbl.std) assert vector[int] == vector(int) v = vector[int]() @@ -501,3 +503,54 @@ assert len(v) == N for i in range(N): assert v[i] == i + + +class AppTestSTLARRAY: + spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools']) + + def setup_class(cls): + cls.w_test_dct = cls.space.newtext(test_dct) + cls.w_stlarray = cls.space.appexec([], """(): + import ctypes, _cppyy + _cppyy._post_import_startup() + return ctypes.CDLL(%r, ctypes.RTLD_GLOBAL)""" % (test_dct, )) + + def test01_array_of_basic_types(self): + """Usage of std::array of basic types""" + + import _cppyy as cppyy + std = cppyy.gbl.std + + a = std.array[int, 4]() + assert len(a) == 4 + for i in range(len(a)): + a[i] = i + assert a[i] == i + + def test02_array_of_pods(self): + """Usage of std::array of PODs""" + + import _cppyy as cppyy + gbl, std = cppyy.gbl, cppyy.gbl.std + + a = std.array[gbl.ArrayTest.Point, 4]() + assert len(a) == 4 + for i in range(len(a)): + a[i].px = i + assert a[i].px == i + a[i].py = i**2 + assert a[i].py == i**2 + + def test03_array_of_pointer_to_pods(self): + """Usage of std::array of pointer to PODs""" + + import cppyy + from cppyy import gbl + from cppyy.gbl import std + + ll = [gbl.ArrayTest.Point() for i in range(4)] + for i in range(len(ll)): + ll[i].px = 13*i + ll[i].py = 42*i + + # more tests in cppyy/test/test_stltypes.py, but currently not supported diff --git a/pypy/module/_multiprocessing/interp_semaphore.py b/pypy/module/_multiprocessing/interp_semaphore.py --- a/pypy/module/_multiprocessing/interp_semaphore.py +++ b/pypy/module/_multiprocessing/interp_semaphore.py @@ -60,7 +60,7 @@ TIMESPEC = platform.Struct('struct timespec', [('tv_sec', rffi.TIME_T), ('tv_nsec', rffi.LONG)]) SEM_FAILED = platform.ConstantInteger('SEM_FAILED') - SEM_VALUE_MAX = platform.ConstantInteger('SEM_VALUE_MAX') + SEM_VALUE_MAX = platform.DefinedConstantInteger('SEM_VALUE_MAX') SEM_TIMED_WAIT = platform.Has('sem_timedwait') SEM_T_SIZE = platform.SizeOf('sem_t') @@ -73,6 +73,8 @@ # rffi.cast(SEM_T, config['SEM_FAILED']) SEM_FAILED = config['SEM_FAILED'] SEM_VALUE_MAX = config['SEM_VALUE_MAX'] + if SEM_VALUE_MAX is None: # on Hurd + SEM_VALUE_MAX = sys.maxint SEM_TIMED_WAIT = config['SEM_TIMED_WAIT'] SEM_T_SIZE = config['SEM_T_SIZE'] if sys.platform == 'darwin': diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py --- a/pypy/module/cpyext/cdatetime.py +++ b/pypy/module/cpyext/cdatetime.py @@ -12,18 +12,19 @@ from pypy.module.__pypy__.interp_pypydatetime import (W_DateTime_Date, W_DateTime_Time, W_DateTime_Delta) from rpython.tool.sourcetools import func_renamer +from pypy.module.cpyext.state import State cts.parse_header(parse_dir / 'cpyext_datetime.h') PyDateTime_CAPI = cts.gettype('PyDateTime_CAPI') -datetimeAPI_global = [] @cpython_api([], lltype.Ptr(PyDateTime_CAPI)) def _PyDateTime_Import(space): - if len(datetimeAPI_global) >0: - return datetimeAPI_global[0] + state = space.fromcache(State) + if len(state.datetimeAPI) > 0: + return state.datetimeAPI[0] datetimeAPI = lltype.malloc(PyDateTime_CAPI, flavor='raw', track_allocation=False) @@ -66,8 +67,8 @@ _PyDelta_FromDelta.api_func.functype, _PyDelta_FromDelta.api_func.get_wrapper(space)) - datetimeAPI_global.append(datetimeAPI) - return datetimeAPI + state.datetimeAPI.append(datetimeAPI) + return state.datetimeAPI[0] PyDateTime_Time = cts.gettype('PyDateTime_Time*') PyDateTime_DateTime = cts.gettype('PyDateTime_DateTime*') @@ -135,8 +136,10 @@ '''Fills a newly allocated py_obj from the w_obj If it is a datetime.time or datetime.datetime, it may have tzinfo ''' - assert len(datetimeAPI_global) > 0 - if datetimeAPI_global[0].c_TimeType == py_obj.c_ob_type: + state = space.fromcache(State) + # cannot raise here, so just crash + assert len(state.datetimeAPI) > 0 + if state.datetimeAPI[0].c_TimeType == py_obj.c_ob_type: py_datetime = rffi.cast(PyDateTime_Time, py_obj) w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo')) if space.is_none(w_tzinfo): @@ -145,7 +148,7 @@ else: py_datetime.c_hastzinfo = cts.cast('unsigned char', 1) py_datetime.c_tzinfo = make_ref(space, w_tzinfo) - elif datetimeAPI_global[0].c_DateTimeType == py_obj.c_ob_type: + elif state.datetimeAPI[0].c_DateTimeType == py_obj.c_ob_type: # For now this is exactly the same structure as PyDateTime_Time py_datetime = rffi.cast(PyDateTime_DateTime, py_obj) w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo')) @@ -159,12 +162,14 @@ @slot_function([PyObject], lltype.Void) def type_dealloc(space, py_obj): from pypy.module.cpyext.object import _dealloc - assert len(datetimeAPI_global) > 0 - if datetimeAPI_global[0].c_TimeType == py_obj.c_ob_type: + state = space.fromcache(State) + # cannot raise here, so just crash + assert len(state.datetimeAPI) > 0 + if state.datetimeAPI[0].c_TimeType == py_obj.c_ob_type: py_datetime = rffi.cast(PyDateTime_Time, py_obj) if (widen(py_datetime.c_hastzinfo) != 0): decref(space, py_datetime.c_tzinfo) - elif datetimeAPI_global[0].c_DateTimeType == py_obj.c_ob_type: + elif state.datetimeAPI[0].c_DateTimeType == py_obj.c_ob_type: py_datetime = rffi.cast(PyDateTime_DateTime, py_obj) if (widen(py_datetime.c_hastzinfo) != 0): decref(space, py_datetime.c_tzinfo) diff --git a/pypy/module/cpyext/state.py b/pypy/module/cpyext/state.py --- a/pypy/module/cpyext/state.py +++ b/pypy/module/cpyext/state.py @@ -42,6 +42,8 @@ # A mapping {filename: copy-of-the-w_dict}, similar to CPython's # variable 'extensions' in Python/import.c. self.extensions = {} + # XXX will leak if _PyDateTime_Import already called + self.datetimeAPI = [] def set_exception(self, operror): self.clear_exception() diff --git a/rpython/jit/backend/detect_cpu.py b/rpython/jit/backend/detect_cpu.py --- a/rpython/jit/backend/detect_cpu.py +++ b/rpython/jit/backend/detect_cpu.py @@ -57,6 +57,7 @@ 'i486': MODEL_X86, 'i586': MODEL_X86, 'i686': MODEL_X86, + 'i686-AT386': MODEL_X86, # Hurd 'i86pc': MODEL_X86, # Solaris/Intel 'x86': MODEL_X86, # Apple 'Power Macintosh': MODEL_PPC_64, diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py --- a/rpython/rlib/buffer.py +++ b/rpython/rlib/buffer.py @@ -6,7 +6,7 @@ from rpython.rtyper.lltypesystem.rstr import STR from rpython.rtyper.lltypesystem.rlist import LIST_OF from rpython.rtyper.annlowlevel import llstr -from rpython.rlib.objectmodel import specialize +from rpython.rlib.objectmodel import specialize, we_are_translated from rpython.rlib import jit from rpython.rlib.rgc import (resizable_list_supporting_raw_ptr, nonmoving_raw_ptr_for_resizable_list, @@ -114,6 +114,12 @@ """ raise CannotWrite + def get_raw_address(self): + msg = "cannot take the raw address of this buffer" + if not we_are_translated(): + msg += " '%s'" % (self,) + raise ValueError(msg) + class RawBuffer(Buffer): """ diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -1086,9 +1086,12 @@ else: return bool(c_func(status)) -WAIT_MACROS = ['WCOREDUMP', 'WIFCONTINUED', 'WIFSTOPPED', +WAIT_MACROS = ['WCOREDUMP', 'WIFSTOPPED', 'WIFSIGNALED', 'WIFEXITED', 'WEXITSTATUS', 'WSTOPSIG', 'WTERMSIG'] +if not sys.platform.startswith('gnu'): + WAIT_MACROS.append('WIFCONTINUED') + for name in WAIT_MACROS: _make_waitmacro(name) diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py --- a/rpython/rlib/rvmprof/cintf.py +++ b/rpython/rlib/rvmprof/cintf.py @@ -17,7 +17,7 @@ # vmprof works only on x86 for now IS_SUPPORTED = False -if sys.platform in ('darwin', 'linux', 'linux2'): +if sys.platform in ('darwin', 'linux', 'linux2') or sys.platform.startswith('freebsd'): try: IS_SUPPORTED = detect_cpu.autodetect().startswith('x86') except detect_cpu.ProcessorAutodetectError: diff --git a/rpython/rlib/rvmprof/dummy.py b/rpython/rlib/rvmprof/dummy.py --- a/rpython/rlib/rvmprof/dummy.py +++ b/rpython/rlib/rvmprof/dummy.py @@ -1,6 +1,7 @@ from rpython.rlib.objectmodel import specialize class DummyVMProf(object): + is_enabled = False def __init__(self): self._unique_id = 0 diff --git a/rpython/rlib/rvmprof/rvmprof.py b/rpython/rlib/rvmprof/rvmprof.py --- a/rpython/rlib/rvmprof/rvmprof.py +++ b/rpython/rlib/rvmprof/rvmprof.py @@ -23,6 +23,7 @@ VMPROF_GC_TAG = 5 class VMProfError(Exception): + msg = '' # annotation hack def __init__(self, msg): self.msg = msg def __str__(self): diff --git a/rpython/rlib/rvmprof/src/shared/vmprof_unix.h b/rpython/rlib/rvmprof/src/shared/vmprof_unix.h --- a/rpython/rlib/rvmprof/src/shared/vmprof_unix.h +++ b/rpython/rlib/rvmprof/src/shared/vmprof_unix.h @@ -24,6 +24,9 @@ #include "vmprof_mt.h" +#ifdef __FreeBSD__ +#include <ucontext.h> +#endif #include <signal.h> RPY_EXTERN void vmprof_ignore_signals(int ignored); diff --git a/rpython/rlib/rvmprof/traceback.py b/rpython/rlib/rvmprof/traceback.py --- a/rpython/rlib/rvmprof/traceback.py +++ b/rpython/rlib/rvmprof/traceback.py @@ -13,6 +13,8 @@ array_length). The caller must free array_p. Not for signal handlers: for these, call vmprof_get_traceback() from C code. """ + if not cintf.IS_SUPPORTED: + return (None, 0) _cintf = rvmprof._get_vmprof().cintf size = estimate_number_of_entries * 2 + 4 stack = cintf.get_rvmprof_stack() @@ -47,6 +49,8 @@ 'code_obj' may be None if it can't be determined. 'loc' is one of the LOC_xxx constants. """ + if not cintf.IS_SUPPORTED: + return i = 0 while i < array_length - 1: tag = array_p[i] diff --git a/rpython/rlib/unicodedata/generate_unicodedb.py b/rpython/rlib/unicodedata/generate_unicodedb.py --- a/rpython/rlib/unicodedata/generate_unicodedb.py +++ b/rpython/rlib/unicodedata/generate_unicodedb.py @@ -913,8 +913,17 @@ casefolds = {} for code, char in table.enum_chars(): - if char.casefolding and char.casefolding != [char.lower]: - casefolds[code] = char.casefolding + full_casefold = char.casefolding + if full_casefold is None: + full_casefold = [code] + full_lower = char.lower + if full_lower is None: + full_lower = code + # if we don't write anything into the file, then the RPython + # program would compute the result 'full_lower' instead. + # Is that the right answer? + if full_casefold != [full_lower]: + casefolds[code] = full_casefold writeDict(outfile, '_casefolds', casefolds, base_mod) print >> outfile, ''' diff --git a/rpython/rlib/unicodedata/test/test_unicodedata.py b/rpython/rlib/unicodedata/test/test_unicodedata.py --- a/rpython/rlib/unicodedata/test/test_unicodedata.py +++ b/rpython/rlib/unicodedata/test/test_unicodedata.py @@ -148,3 +148,15 @@ def test_changed_in_version_8(self): assert unicodedb_6_2_0.toupper_full(0x025C) == [0x025C] assert unicodedb_8_0_0.toupper_full(0x025C) == [0xA7AB] + + def test_casefold(self): + # returns None when we have no special casefolding rule, + # which means that tolower_full() should be used instead + assert unicodedb_8_0_0.casefold_lookup(0x1000) == None + assert unicodedb_8_0_0.casefold_lookup(0x0061) == None + assert unicodedb_8_0_0.casefold_lookup(0x0041) == None + # a case where casefold() != lower() + assert unicodedb_8_0_0.casefold_lookup(0x00DF) == [ord('s'), ord('s')] + # returns the argument itself, and not None, in rare cases + # where tolower_full() would return something different + assert unicodedb_8_0_0.casefold_lookup(0x13A0) == [0x13A0] diff --git a/rpython/rlib/unicodedata/unicodedb_8_0_0.py b/rpython/rlib/unicodedata/unicodedb_8_0_0.py --- a/rpython/rlib/unicodedata/unicodedb_8_0_0.py +++ b/rpython/rlib/unicodedata/unicodedb_8_0_0.py @@ -21307,6 +21307,92 @@ return code _casefolds = { +5024: [5024], +5025: [5025], +5026: [5026], +5027: [5027], +5028: [5028], +5029: [5029], +5030: [5030], +5031: [5031], +5032: [5032], +5033: [5033], +5034: [5034], +5035: [5035], +5036: [5036], +5037: [5037], +5038: [5038], +5039: [5039], +5040: [5040], +5041: [5041], +5042: [5042], +5043: [5043], +5044: [5044], +5045: [5045], +5046: [5046], +5047: [5047], +5048: [5048], +5049: [5049], +5050: [5050], +5051: [5051], +5052: [5052], +5053: [5053], +5054: [5054], +5055: [5055], +5056: [5056], _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit