Author: Armin Rigo <ar...@tunes.org> Branch: share-cpyext-cpython-api Changeset: r84055:4c3d9f56e6d4 Date: 2016-04-30 11:32 +0100 http://bitbucket.org/pypy/pypy/changeset/4c3d9f56e6d4/
Log: hg merge default diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst --- a/pypy/doc/faq.rst +++ b/pypy/doc/faq.rst @@ -117,13 +117,22 @@ On which platforms does PyPy run? --------------------------------- -PyPy is regularly and extensively tested on Linux machines. It mostly +PyPy currently supports: + + * **x86** machines on most common operating systems + (Linux 32/64 bits, Mac OS X 64 bits, Windows 32 bits, OpenBSD, FreeBSD), + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +PyPy is regularly and extensively tested on Linux machines. It works on Mac and Windows: it is tested there, but most of us are running -Linux so fixes may depend on 3rd-party contributions. PyPy's JIT -works on x86 (32-bit or 64-bit) and on ARM (ARMv6 or ARMv7). -Support for POWER (64-bit) is stalled at the moment. +Linux so fixes may depend on 3rd-party contributions. -To bootstrap from sources, PyPy can use either CPython (2.6 or 2.7) or +To bootstrap from sources, PyPy can use either CPython 2.7 or another (e.g. older) PyPy. Cross-translation is not really supported: e.g. to build a 32-bit PyPy, you need to have a 32-bit environment. Cross-translation is only explicitly supported between a 32-bit Intel diff --git a/pypy/interpreter/test/test_typedef.py b/pypy/interpreter/test/test_typedef.py --- a/pypy/interpreter/test/test_typedef.py +++ b/pypy/interpreter/test/test_typedef.py @@ -362,6 +362,45 @@ """) assert seen == [1] + def test_mapdict_number_of_slots(self): + space = self.space + a, b, c = space.unpackiterable(space.appexec([], """(): + class A(object): + pass + a = A() + a.x = 1 + class B: + pass + b = B() + b.x = 1 + class C(int): + pass + c = C(1) + c.x = 1 + return a, b, c + """), 3) + assert not hasattr(a, "storage") + assert not hasattr(b, "storage") + assert hasattr(c, "storage") + + def test_del(self): + space = self.space + a, b, c, d = space.unpackiterable(space.appexec([], """(): + class A(object): + pass + class B(object): + def __del__(self): + pass + class F(file): + pass + class G(file): + def __del__(self): + pass + return A(), B(), F("xyz", "w"), G("ghi", "w") + """)) + assert type(b).__base__ is type(a) + assert hasattr(c, "__del__") + assert type(d) is type(c) class AppTestTypeDef: diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -103,43 +103,61 @@ # we need two subclasses of the app-level type, one to add mapdict, and then one # to add del to not slow down the GC. -def get_unique_interplevel_subclass(config, cls, needsdel=False): +def get_unique_interplevel_subclass(space, cls, needsdel=False): "NOT_RPYTHON: initialization-time only" if hasattr(cls, '__del__') and getattr(cls, "handle_del_manually", False): needsdel = False assert cls.typedef.acceptable_as_base_class - key = config, cls, needsdel + key = space, cls, needsdel try: return _subclass_cache[key] except KeyError: # XXX can save a class if cls already has a __del__ - if needsdel: - cls = get_unique_interplevel_subclass(config, cls, False) - subcls = _getusercls(config, cls, needsdel) + keys = [key] + base_has_del = hasattr(cls, '__del__') + if base_has_del: + # if the base has a __del__, we only need one class + keys = [(space, cls, True), (space, cls, False)] + needsdel = True + elif needsdel: + cls = get_unique_interplevel_subclass(space, cls, False) + subcls = _getusercls(space, cls, needsdel) assert key not in _subclass_cache - _subclass_cache[key] = subcls + for key in keys: + _subclass_cache[key] = subcls return subcls get_unique_interplevel_subclass._annspecialcase_ = "specialize:memo" _subclass_cache = {} -def _getusercls(config, cls, wants_del, reallywantdict=False): +def _getusercls(space, cls, wants_del, reallywantdict=False): from rpython.rlib import objectmodel + from pypy.objspace.std.objectobject import W_ObjectObject + from pypy.module.__builtin__.interp_classobj import W_InstanceObject from pypy.objspace.std.mapdict import (BaseUserClassMapdict, MapdictDictSupport, MapdictWeakrefSupport, - _make_storage_mixin_size_n) + _make_storage_mixin_size_n, MapdictStorageMixin) typedef = cls.typedef name = cls.__name__ + "User" - mixins_needed = [BaseUserClassMapdict, _make_storage_mixin_size_n()] - if reallywantdict or not typedef.hasdict: - # the type has no dict, mapdict to provide the dict - mixins_needed.append(MapdictDictSupport) - name += "Dict" - if not typedef.weakrefable: - # the type does not support weakrefs yet, mapdict to provide weakref - # support - mixins_needed.append(MapdictWeakrefSupport) - name += "Weakrefable" + mixins_needed = [] + copy_methods = [] + mixins_needed = [] + name = cls.__name__ + if not cls.user_overridden_class: + if cls is W_ObjectObject or cls is W_InstanceObject: + mixins_needed.append(_make_storage_mixin_size_n()) + else: + mixins_needed.append(MapdictStorageMixin) + copy_methods = [BaseUserClassMapdict] + if reallywantdict or not typedef.hasdict: + # the type has no dict, mapdict to provide the dict + copy_methods.append(MapdictDictSupport) + name += "Dict" + if not typedef.weakrefable: + # the type does not support weakrefs yet, mapdict to provide weakref + # support + copy_methods.append(MapdictWeakrefSupport) + name += "Weakrefable" if wants_del: name += "Del" parent_destructor = getattr(cls, '__del__', None) @@ -148,14 +166,14 @@ parent_destructor(self) def call_applevel_del(self): assert isinstance(self, subcls) - self.space.userdel(self) + space.userdel(self) class Proto(object): def __del__(self): self.clear_all_weakrefs() - self.enqueue_for_destruction(self.space, call_applevel_del, + self.enqueue_for_destruction(space, call_applevel_del, 'method __del__ of ') if parent_destructor is not None: - self.enqueue_for_destruction(self.space, call_parent_del, + self.enqueue_for_destruction(space, call_parent_del, 'internal destructor of ') mixins_needed.append(Proto) @@ -163,10 +181,17 @@ user_overridden_class = True for base in mixins_needed: objectmodel.import_from_mixin(base) + for copycls in copy_methods: + _copy_methods(copycls, subcls) del subcls.base subcls.__name__ = name return subcls +def _copy_methods(copycls, subcls): + for key, value in copycls.__dict__.items(): + if (not key.startswith('__') or key == '__del__'): + setattr(subcls, key, value) + # ____________________________________________________________ diff --git a/pypy/module/__builtin__/interp_classobj.py b/pypy/module/__builtin__/interp_classobj.py --- a/pypy/module/__builtin__/interp_classobj.py +++ b/pypy/module/__builtin__/interp_classobj.py @@ -195,9 +195,9 @@ return self.cls_without_del = _getusercls( - space.config, W_InstanceObject, False, reallywantdict=True) + space, W_InstanceObject, False, reallywantdict=True) self.cls_with_del = _getusercls( - space.config, W_InstanceObject, True, reallywantdict=True) + space, W_InstanceObject, True, reallywantdict=True) def class_descr_call(space, w_self, __args__): diff --git a/pypy/module/cpyext/ndarrayobject.py b/pypy/module/cpyext/ndarrayobject.py --- a/pypy/module/cpyext/ndarrayobject.py +++ b/pypy/module/cpyext/ndarrayobject.py @@ -239,9 +239,7 @@ gufunctype = lltype.Ptr(ufuncs.GenericUfunc) -# XXX single rffi.CArrayPtr(gufunctype) does not work, this does, is there -# a problem with casting function pointers? -@cpython_api([rffi.CArrayPtr(rffi.CArrayPtr(gufunctype)), rffi.VOIDP, rffi.CCHARP, Py_ssize_t, Py_ssize_t, +@cpython_api([rffi.CArrayPtr(gufunctype), rffi.VOIDP, rffi.CCHARP, Py_ssize_t, Py_ssize_t, Py_ssize_t, Py_ssize_t, rffi.CCHARP, rffi.CCHARP, Py_ssize_t, rffi.CCHARP], PyObject, header=HEADER) def PyUFunc_FromFuncAndDataAndSignature(space, funcs, data, types, ntypes, @@ -256,7 +254,7 @@ funcs_w = [None] * ntypes dtypes_w = [None] * ntypes * (nin + nout) for i in range(ntypes): - funcs_w[i] = ufuncs.W_GenericUFuncCaller(rffi.cast(gufunctype, funcs[i]), data) + funcs_w[i] = ufuncs.W_GenericUFuncCaller(funcs[i], data) for i in range(ntypes*(nin+nout)): dtypes_w[i] = get_dtype_cache(space).dtypes_by_num[ord(types[i])] w_funcs = space.newlist(funcs_w) @@ -268,7 +266,7 @@ w_signature, w_identity, w_name, w_doc, stack_inputs=True) return ufunc_generic -@cpython_api([rffi.CArrayPtr(rffi.CArrayPtr(gufunctype)), rffi.VOIDP, rffi.CCHARP, Py_ssize_t, Py_ssize_t, +@cpython_api([rffi.CArrayPtr(gufunctype), rffi.VOIDP, rffi.CCHARP, Py_ssize_t, Py_ssize_t, Py_ssize_t, Py_ssize_t, rffi.CCHARP, rffi.CCHARP, Py_ssize_t], PyObject, header=HEADER) def PyUFunc_FromFuncAndData(space, funcs, data, types, ntypes, nin, nout, identity, name, doc, check_return): diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -254,13 +254,15 @@ class AppTestCpythonExtensionBase(LeakCheckingTest): def setup_class(cls): - cls.space.getbuiltinmodule("cpyext") - from pypy.module.imp.importing import importhook - importhook(cls.space, "os") # warm up reference counts + space = cls.space + space.getbuiltinmodule("cpyext") + # 'import os' to warm up reference counts + w_import = space.builtin.getdictvalue(space, '__import__') + space.call_function(w_import, space.wrap("os")) #state = cls.space.fromcache(RefcountState) ZZZ #state.non_heaptypes_w[:] = [] if not cls.runappdirect: - cls.w_runappdirect = cls.space.wrap(cls.runappdirect) + cls.w_runappdirect = space.wrap(cls.runappdirect) def setup_method(self, func): @gateway.unwrap_spec(name=str) diff --git a/pypy/module/cpyext/test/test_ndarrayobject.py b/pypy/module/cpyext/test/test_ndarrayobject.py --- a/pypy/module/cpyext/test/test_ndarrayobject.py +++ b/pypy/module/cpyext/test/test_ndarrayobject.py @@ -366,7 +366,7 @@ def test_ufunc(self): if self.runappdirect: from numpy import arange - py.test.xfail('why does this segfault on cpython?') + py.test.xfail('segfaults on cpython: PyUFunc_API == NULL?') else: from _numpypy.multiarray import arange mod = self.import_extension('foo', [ diff --git a/pypy/objspace/std/mapdict.py b/pypy/objspace/std/mapdict.py --- a/pypy/objspace/std/mapdict.py +++ b/pypy/objspace/std/mapdict.py @@ -277,7 +277,7 @@ def copy(self, obj): result = Object() result.space = self.space - result._init_empty(self) + result._mapdict_init_empty(self) return result def length(self): @@ -286,7 +286,7 @@ def set_terminator(self, obj, terminator): result = Object() result.space = self.space - result._init_empty(terminator) + result._mapdict_init_empty(terminator) return result def remove_dict_entries(self, obj): @@ -304,7 +304,7 @@ def materialize_r_dict(self, space, obj, dict_w): result = Object() result.space = space - result._init_empty(self.devolved_dict_terminator) + result._mapdict_init_empty(self.devolved_dict_terminator) return result @@ -417,11 +417,6 @@ def __repr__(self): return "<PlainAttribute %s %s %s %r>" % (self.name, self.index, self.storageindex, self.back) -def _become(w_obj, new_obj): - # this is like the _become method, really, but we cannot use that due to - # RPython reasons - w_obj._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) - class MapAttrCache(object): def __init__(self, space): SIZE = 1 << space.config.objspace.std.methodcachesizeexp @@ -457,22 +452,12 @@ # everything that's needed to use mapdict for a user subclass at all. # This immediately makes slots possible. - # assumes presence of _init_empty, _mapdict_read_storage, + # assumes presence of _get_mapdict_map, _set_mapdict_map + # _mapdict_init_empty, _mapdict_read_storage, # _mapdict_write_storage, _mapdict_storage_length, # _set_mapdict_storage_and_map # _____________________________________________ - # methods needed for mapdict - - def _become(self, new_obj): - self._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) - - def _get_mapdict_map(self): - return jit.promote(self.map) - def _set_mapdict_map(self, map): - self.map = map - - # _____________________________________________ # objspace interface # class access @@ -482,15 +467,14 @@ def setclass(self, space, w_cls): new_obj = self._get_mapdict_map().set_terminator(self, w_cls.terminator) - self._become(new_obj) + self._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) def user_setup(self, space, w_subtype): from pypy.module.__builtin__.interp_classobj import W_InstanceObject - self.space = space assert (not self.typedef.hasdict or isinstance(w_subtype.terminator, NoDictTerminator) or self.typedef is W_InstanceObject.typedef) - self._init_empty(w_subtype.terminator) + self._mapdict_init_empty(w_subtype.terminator) # methods needed for slots @@ -508,7 +492,7 @@ new_obj = self._get_mapdict_map().delete(self, "slot", index) if new_obj is None: return False - self._become(new_obj) + self._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) return True @@ -549,7 +533,7 @@ new_obj = self._get_mapdict_map().delete(self, attrname, DICT) if new_obj is None: return False - self._become(new_obj) + self._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) return True def getdict(self, space): @@ -599,7 +583,12 @@ assert flag class MapdictStorageMixin(object): - def _init_empty(self, map): + def _get_mapdict_map(self): + return jit.promote(self.map) + def _set_mapdict_map(self, map): + self.map = map + + def _mapdict_init_empty(self, map): from rpython.rlib.debug import make_sure_not_resized self.map = map self.storage = make_sure_not_resized([None] * map.size_estimate()) @@ -613,6 +602,7 @@ def _mapdict_storage_length(self): return len(self.storage) + def _set_mapdict_storage_and_map(self, storage, map): self.storage = storage self.map = map @@ -643,7 +633,11 @@ rangenmin1 = unroll.unrolling_iterable(range(nmin1)) valnmin1 = "_value%s" % nmin1 class subcls(object): - def _init_empty(self, map): + def _get_mapdict_map(self): + return jit.promote(self.map) + def _set_mapdict_map(self, map): + self.map = map + def _mapdict_init_empty(self, map): for i in rangenmin1: setattr(self, "_value%s" % i, None) setattr(self, valnmin1, erase_item(None)) @@ -731,7 +725,7 @@ def get_empty_storage(self): w_result = Object() terminator = self.space.fromcache(get_terminator_for_dicts) - w_result._init_empty(terminator) + w_result._mapdict_init_empty(terminator) return self.erase(w_result) def switch_to_object_strategy(self, w_dict): @@ -811,7 +805,7 @@ def clear(self, w_dict): w_obj = self.unerase(w_dict.dstorage) new_obj = w_obj._get_mapdict_map().remove_dict_entries(w_obj) - _become(w_obj, new_obj) + w_obj._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) def popitem(self, w_dict): curr = self.unerase(w_dict.dstorage)._get_mapdict_map().search(DICT) @@ -836,7 +830,7 @@ def materialize_r_dict(space, obj, dict_w): map = obj._get_mapdict_map() new_obj = map.materialize_r_dict(space, obj, dict_w) - _become(obj, new_obj) + obj._set_mapdict_storage_and_map(new_obj.storage, new_obj.map) class MapDictIteratorKeys(BaseKeyIterator): def __init__(self, space, strategy, dictimplementation): diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py --- a/pypy/objspace/std/objspace.py +++ b/pypy/objspace/std/objspace.py @@ -358,7 +358,7 @@ cls = cls.typedef.applevel_subclasses_base # subcls = get_unique_interplevel_subclass( - self.config, cls, w_subtype.needsdel) + self, cls, w_subtype.needsdel) instance = instantiate(subcls) assert isinstance(instance, cls) instance.user_setup(self, w_subtype) diff --git a/rpython/rtyper/lltypesystem/ll2ctypes.py b/rpython/rtyper/lltypesystem/ll2ctypes.py --- a/rpython/rtyper/lltypesystem/ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/ll2ctypes.py @@ -231,17 +231,7 @@ assert max_n >= 0 ITEM = A.OF ctypes_item = get_ctypes_type(ITEM, delayed_builders) - # Python 2.5 ctypes can raise OverflowError on 64-bit builds - for n in [maxint, 2**31]: - MAX_SIZE = n/64 - try: - PtrType = ctypes.POINTER(MAX_SIZE * ctypes_item) - except (OverflowError, AttributeError), e: - pass # ^^^ bah, blame ctypes - else: - break - else: - raise e + ctypes_item_ptr = ctypes.POINTER(ctypes_item) class CArray(ctypes.Structure): if is_emulated_long: @@ -265,35 +255,9 @@ bigarray.length = n return bigarray - _ptrtype = None - - @classmethod - def _get_ptrtype(cls): - if cls._ptrtype: - return cls._ptrtype - # ctypes can raise OverflowError on 64-bit builds - # on windows it raises AttributeError even for 2**31 (_length_ missing) - if _MS_WINDOWS: - other_limit = 2**31-1 - else: - other_limit = 2**31 - for n in [maxint, other_limit]: - cls.MAX_SIZE = n / ctypes.sizeof(ctypes_item) - try: - cls._ptrtype = ctypes.POINTER(cls.MAX_SIZE * ctypes_item) - except (OverflowError, AttributeError), e: - pass - else: - break - else: - raise e - return cls._ptrtype - def _indexable(self, index): - PtrType = self._get_ptrtype() - assert index + 1 < self.MAX_SIZE - p = ctypes.cast(ctypes.pointer(self.items), PtrType) - return p.contents + p = ctypes.cast(self.items, ctypes_item_ptr) + return p def _getitem(self, index, boundscheck=True): if boundscheck: @@ -1045,12 +1009,22 @@ container = _array_of_known_length(T.TO) container._storage = type(cobj)(cobj.contents) elif isinstance(T.TO, lltype.FuncType): + # cobj is a CFunctionType object. We naively think + # that it should be a function pointer. No no no. If + # it was read out of an array, say, then it is a *pointer* + # to a function pointer. In other words, the read doesn't + # read anything, it just takes the address of the function + # pointer inside the array. If later the array is modified + # or goes out of scope, then we crash. CTypes is fun. + # It works if we cast it now to an int and back. cobjkey = intmask(ctypes.cast(cobj, ctypes.c_void_p).value) if cobjkey in _int2obj: container = _int2obj[cobjkey] else: + name = getattr(cobj, '__name__', '?') + cobj = ctypes.cast(cobjkey, type(cobj)) _callable = get_ctypes_trampoline(T.TO, cobj) - return lltype.functionptr(T.TO, getattr(cobj, '__name__', '?'), + return lltype.functionptr(T.TO, name, _callable=_callable) elif isinstance(T.TO, lltype.OpaqueType): if T == llmemory.GCREF: diff --git a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py --- a/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py +++ b/rpython/rtyper/lltypesystem/test/test_ll2ctypes.py @@ -1405,6 +1405,45 @@ a2 = ctypes2lltype(lltype.Ptr(A), lltype2ctypes(a)) assert a2._obj.getitem(0)._obj._parentstructure() is a2._obj + def test_array_of_function_pointers(self): + c_source = py.code.Source(r""" + #include "src/precommondefs.h" + #include <stdio.h> + + typedef int(*funcptr_t)(void); + static int forty_two(void) { return 42; } + static int forty_three(void) { return 43; } + static funcptr_t testarray[2]; + RPY_EXPORTED void runtest(void cb(funcptr_t *)) { + testarray[0] = &forty_two; + testarray[1] = &forty_three; + fprintf(stderr, "&forty_two = %p\n", testarray[0]); + fprintf(stderr, "&forty_three = %p\n", testarray[1]); + cb(testarray); + testarray[0] = 0; + testarray[1] = 0; + } + """) + eci = ExternalCompilationInfo(include_dirs=[cdir], + separate_module_sources=[c_source]) + + PtrF = lltype.Ptr(lltype.FuncType([], rffi.INT)) + ArrayPtrF = rffi.CArrayPtr(PtrF) + CALLBACK = rffi.CCallback([ArrayPtrF], lltype.Void) + + runtest = rffi.llexternal('runtest', [CALLBACK], lltype.Void, + compilation_info=eci) + seen = [] + + def callback(testarray): + seen.append(testarray[0]) # read a PtrF out of testarray + seen.append(testarray[1]) + + runtest(callback) + assert seen[0]() == 42 + assert seen[1]() == 43 + + class TestPlatform(object): def test_lib_on_libpaths(self): from rpython.translator.platform import platform _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit