Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r86938:5db8cf7e71cd Date: 2016-09-07 15:24 +0100 http://bitbucket.org/pypy/pypy/changeset/5db8cf7e71cd/
Log: hg merge py3k diff too long, truncating to 2000 out of 2403 lines diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -30,3 +30,6 @@ 68bb3510d8212ae9efb687e12e58c09d29e74f87 release-pypy2.7-v5.4.0 68bb3510d8212ae9efb687e12e58c09d29e74f87 release-pypy2.7-v5.4.0 77392ad263504df011ccfcabf6a62e21d04086d0 release-pypy2.7-v5.4.0 +050d84dd78997f021acf0e133934275d63547cc0 release-pypy2.7-v5.4.1 +050d84dd78997f021acf0e133934275d63547cc0 release-pypy2.7-v5.4.1 +0e2d9a73f5a1818d0245d75daccdbe21b2d5c3ef release-pypy2.7-v5.4.1 diff --git a/_pytest/python.py b/_pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -498,7 +498,10 @@ """ Collector for test methods. """ def collect(self): if hasinit(self.obj): - pytest.skip("class %s.%s with __init__ won't get collected" % ( + # XXX used to be skip(), but silently skipping classes + # XXX just because they have been written long ago is + # XXX imho a very, very, very bad idea + pytest.fail("class %s.%s with __init__ won't get collected" % ( self.obj.__module__, self.obj.__name__, )) diff --git a/lib_pypy/cffi.egg-info/PKG-INFO b/lib_pypy/cffi.egg-info/PKG-INFO --- a/lib_pypy/cffi.egg-info/PKG-INFO +++ b/lib_pypy/cffi.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: cffi -Version: 1.8.0 +Version: 1.8.2 Summary: Foreign Function Interface for Python calling C code. Home-page: http://cffi.readthedocs.org Author: Armin Rigo, Maciej Fijalkowski diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py --- a/lib_pypy/cffi/__init__.py +++ b/lib_pypy/cffi/__init__.py @@ -4,8 +4,8 @@ from .api import FFI, CDefError, FFIError from .ffiplatform import VerificationError, VerificationMissing -__version__ = "1.8.0" -__version_info__ = (1, 8, 0) +__version__ = "1.8.2" +__version_info__ = (1, 8, 2) # The verifier module file names are based on the CRC32 of a string that # contains the following version number. It may be older than __version__ diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h --- a/lib_pypy/cffi/_cffi_include.h +++ b/lib_pypy/cffi/_cffi_include.h @@ -1,4 +1,20 @@ #define _CFFI_ + +/* We try to define Py_LIMITED_API before including Python.h. + + Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and + Py_REF_DEBUG are not defined. This is a best-effort approximation: + we can learn about Py_DEBUG from pyconfig.h, but it is unclear if + the same works for the other two macros. Py_DEBUG implies them, + but not the other way around. +*/ +#ifndef _CFFI_USE_EMBEDDING +# include <pyconfig.h> +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) +# define Py_LIMITED_API +# endif +#endif + #include <Python.h> #ifdef __cplusplus extern "C" { diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h --- a/lib_pypy/cffi/_embedding.h +++ b/lib_pypy/cffi/_embedding.h @@ -233,7 +233,7 @@ f = PySys_GetObject((char *)"stderr"); if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.8.0" + "\ncompiled with cffi version: 1.8.2" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py --- a/lib_pypy/cffi/api.py +++ b/lib_pypy/cffi/api.py @@ -652,7 +652,7 @@ recompile(self, module_name, source, c_file=filename, call_c_compiler=False, **kwds) - def compile(self, tmpdir='.', verbose=0, target=None): + def compile(self, tmpdir='.', verbose=0, target=None, debug=None): """The 'target' argument gives the final file name of the compiled DLL. Use '*' to force distutils' choice, suitable for regular CPython C API modules. Use a file name ending in '.*' @@ -669,7 +669,7 @@ module_name, source, source_extension, kwds = self._assigned_source return recompile(self, module_name, source, tmpdir=tmpdir, target=target, source_extension=source_extension, - compiler_verbose=verbose, **kwds) + compiler_verbose=verbose, debug=debug, **kwds) def init_once(self, func, tag): # Read _init_once_cache[tag], which is either (False, lock) if diff --git a/lib_pypy/cffi/backend_ctypes.py b/lib_pypy/cffi/backend_ctypes.py --- a/lib_pypy/cffi/backend_ctypes.py +++ b/lib_pypy/cffi/backend_ctypes.py @@ -997,29 +997,43 @@ assert onerror is None # XXX not implemented return BType(source, error) + _weakref_cache_ref = None + def gcp(self, cdata, destructor): - BType = self.typeof(cdata) + if self._weakref_cache_ref is None: + import weakref + class MyRef(weakref.ref): + def __eq__(self, other): + myref = self() + return self is other or ( + myref is not None and myref is other()) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self()) + return self._hash + self._weakref_cache_ref = {}, MyRef + weak_cache, MyRef = self._weakref_cache_ref if destructor is None: - if not (hasattr(BType, '_gcp_type') and - BType._gcp_type is BType): + try: + del weak_cache[MyRef(cdata)] + except KeyError: raise TypeError("Can remove destructor only on a object " "previously returned by ffi.gc()") - cdata._destructor = None return None - try: - gcp_type = BType._gcp_type - except AttributeError: - class CTypesDataGcp(BType): - __slots__ = ['_orig', '_destructor'] - def __del__(self): - if self._destructor is not None: - self._destructor(self._orig) - gcp_type = BType._gcp_type = CTypesDataGcp - new_cdata = self.cast(gcp_type, cdata) - new_cdata._orig = cdata - new_cdata._destructor = destructor + def remove(k): + cdata, destructor = weak_cache.pop(k, (None, None)) + if destructor is not None: + destructor(cdata) + + new_cdata = self.cast(self.typeof(cdata), cdata) + assert new_cdata is not cdata + weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) return new_cdata typeof = type diff --git a/lib_pypy/cffi/ffiplatform.py b/lib_pypy/cffi/ffiplatform.py --- a/lib_pypy/cffi/ffiplatform.py +++ b/lib_pypy/cffi/ffiplatform.py @@ -21,12 +21,12 @@ allsources.append(os.path.normpath(src)) return Extension(name=modname, sources=allsources, **kwds) -def compile(tmpdir, ext, compiler_verbose=0): +def compile(tmpdir, ext, compiler_verbose=0, debug=None): """Compile a C extension module using distutils.""" saved_environ = os.environ.copy() try: - outputfilename = _build(tmpdir, ext, compiler_verbose) + outputfilename = _build(tmpdir, ext, compiler_verbose, debug) outputfilename = os.path.abspath(outputfilename) finally: # workaround for a distutils bugs where some env vars can @@ -36,7 +36,7 @@ os.environ[key] = value return outputfilename -def _build(tmpdir, ext, compiler_verbose=0): +def _build(tmpdir, ext, compiler_verbose=0, debug=None): # XXX compact but horrible :-( from distutils.core import Distribution import distutils.errors, distutils.log @@ -44,6 +44,9 @@ dist = Distribution({'ext_modules': [ext]}) dist.parse_config_files() options = dist.get_option_dict('build_ext') + if debug is None: + debug = sys.flags.debug + options['debug'] = ('ffiplatform', debug) options['force'] = ('ffiplatform', True) options['build_lib'] = ('ffiplatform', tmpdir) options['build_temp'] = ('ffiplatform', tmpdir) diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py --- a/lib_pypy/cffi/recompiler.py +++ b/lib_pypy/cffi/recompiler.py @@ -275,8 +275,8 @@ def write_c_source_to_f(self, f, preamble): self._f = f prnt = self._prnt - if self.ffi._embedding is None: - prnt('#define Py_LIMITED_API') + if self.ffi._embedding is not None: + prnt('#define _CFFI_USE_EMBEDDING') # # first the '#include' (actually done by inlining the file's content) lines = self._rel_readlines('_cffi_include.h') @@ -1431,7 +1431,7 @@ def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, c_file=None, source_extension='.c', extradir=None, - compiler_verbose=1, target=None, **kwds): + compiler_verbose=1, target=None, debug=None, **kwds): if not isinstance(module_name, str): module_name = module_name.encode('ascii') if ffi._windows_unicode: @@ -1467,7 +1467,8 @@ if target != '*': _patch_for_target(patchlist, target) os.chdir(tmpdir) - outputfilename = ffiplatform.compile('.', ext, compiler_verbose) + outputfilename = ffiplatform.compile('.', ext, + compiler_verbose, debug) finally: os.chdir(cwd) _unpatch_meths(patchlist) diff --git a/lib_pypy/cffi/setuptools_ext.py b/lib_pypy/cffi/setuptools_ext.py --- a/lib_pypy/cffi/setuptools_ext.py +++ b/lib_pypy/cffi/setuptools_ext.py @@ -69,16 +69,36 @@ else: _add_c_module(dist, ffi, module_name, source, source_extension, kwds) +def _set_py_limited_api(Extension, kwds): + """ + Add py_limited_api to kwds if setuptools >= 26 is in use. + Do not alter the setting if it already exists. + Setuptools takes care of ignoring the flag on Python 2 and PyPy. + """ + if 'py_limited_api' not in kwds: + import setuptools + try: + setuptools_major_version = int(setuptools.__version__.partition('.')[0]) + if setuptools_major_version >= 26: + kwds['py_limited_api'] = True + except ValueError: # certain development versions of setuptools + # If we don't know the version number of setuptools, we + # try to set 'py_limited_api' anyway. At worst, we get a + # warning. + kwds['py_limited_api'] = True + return kwds def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): from distutils.core import Extension - from distutils.command.build_ext import build_ext + # We are a setuptools extension. Need this build_ext for py_limited_api. + from setuptools.command.build_ext import build_ext from distutils.dir_util import mkpath from distutils import log from cffi import recompiler allsources = ['$PLACEHOLDER'] allsources.extend(kwds.pop('sources', [])) + kwds = _set_py_limited_api(Extension, kwds) ext = Extension(name=module_name, sources=allsources, **kwds) def make_mod(tmpdir, pre_run=None): diff --git a/pypy/doc/index-of-release-notes.rst b/pypy/doc/index-of-release-notes.rst --- a/pypy/doc/index-of-release-notes.rst +++ b/pypy/doc/index-of-release-notes.rst @@ -6,6 +6,7 @@ .. toctree:: + release-pypy2.7-v5.4.1.rst release-pypy2.7-v5.4.0.rst release-pypy2.7-v5.3.1.rst release-pypy2.7-v5.3.0.rst diff --git a/pypy/doc/release-pypy2.7-v5.4.1.rst b/pypy/doc/release-pypy2.7-v5.4.1.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-pypy2.7-v5.4.1.rst @@ -0,0 +1,64 @@ +========== +PyPy 5.4.1 +========== + +We have released a bugfix for PyPy2.7-v5.4.0, released last week, +due to the following issues: + + * Update list of contributors in documentation and LICENSE file, + this was unfortunately left out of 5.4.0. My apologies to the new + contributors + + * Allow tests run with `-A` to find `libm.so` even if it is a script not a + dynamically loadable file + + * Bump `sys.setrecursionlimit()` when translating PyPy, for translating with CPython + + * Tweak a float comparison with 0 in `backendopt.inline` to avoid rounding errors + + * Fix for an issue where os.access() accepted a float for mode + + * Fix for and issue where `unicode.decode('utf8', 'custom_replace')` messed up + the last byte of a unicode string sometimes + + * Update built-in cffi_ to version 1.8.1 + + * Explicitly detect that we found as-yet-unsupported OpenSSL 1.1, and crash + translation with a message asking for help porting it + + * Fix a regression where a PyBytesObject was forced (converted to a RPython + object) when not required, reported as issue #2395 + +Thanks to those who reported the issues. + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7. It's fast (`PyPy and CPython 2.7.x`_ performance comparison) +due to its integrated tracing JIT compiler. + +We also welcome developers of other +`dynamic languages`_ to see what RPython can do for them. + +This release supports: + + * **x86** machines on most common operating systems + (Linux 32/64, Mac OS X 64, Windows 32, OpenBSD, FreeBSD), + + * newer **ARM** hardware (ARMv6 or ARMv7, with VFPv3) running Linux, + + * big- and little-endian variants of **PPC64** running Linux, + + * **s390x** running Linux + +.. _cffi: https://cffi.readthedocs.io +.. _`PyPy and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://pypyjs.org + +Please update, and continue to help us make PyPy better. + +Cheers + +The PyPy Team + 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,3 +7,9 @@ .. branch: rpython-resync Backport rpython changes made directly on the py3k and py3.5 branches. + +.. branch: buffer-interface +Implement PyObject_GetBuffer, PyMemoryView_GET_BUFFER, and handles memoryviews +in numpypy + + diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1437,6 +1437,9 @@ BUF_FORMAT = 0x0004 BUF_ND = 0x0008 BUF_STRIDES = 0x0010 | BUF_ND + BUF_C_CONTIGUOUS = 0x0020 | BUF_STRIDES + BUF_F_CONTIGUOUS = 0x0040 | BUF_STRIDES + BUF_ANY_CONTIGUOUS = 0x0080 | BUF_STRIDES BUF_INDIRECT = 0x0100 | BUF_STRIDES BUF_CONTIG_RO = BUF_ND diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -3,7 +3,7 @@ from rpython.rlib import rdynload, clibffi, entrypoint from rpython.rtyper.lltypesystem import rffi -VERSION = "1.8.0" +VERSION = "1.8.2" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py --- a/pypy/module/_cffi_backend/ctypestruct.py +++ b/pypy/module/_cffi_backend/ctypestruct.py @@ -105,9 +105,6 @@ return True return False - def _check_only_one_argument_for_union(self, w_ob): - pass - def convert_from_object(self, cdata, w_ob): if not self._copy_from_same(cdata, w_ob): self.convert_struct_from_object(cdata, w_ob, optvarsize=-1) @@ -117,19 +114,24 @@ ) def convert_struct_from_object(self, cdata, w_ob, optvarsize): self.force_lazy_struct() - self._check_only_one_argument_for_union(w_ob) space = self.space if (space.isinstance_w(w_ob, space.w_list) or space.isinstance_w(w_ob, space.w_tuple)): lst_w = space.listview(w_ob) - if len(lst_w) > len(self._fields_list): - raise oefmt(space.w_ValueError, - "too many initializers for '%s' (got %d)", - self.name, len(lst_w)) - for i in range(len(lst_w)): - optvarsize = self._fields_list[i].write_v(cdata, lst_w[i], + j = 0 + for w_obj in lst_w: + try: + while (self._fields_list[j].flags & + W_CField.BF_IGNORE_IN_CTOR): + j += 1 + except IndexError: + raise oefmt(space.w_ValueError, + "too many initializers for '%s' (got %d)", + self.name, len(lst_w)) + optvarsize = self._fields_list[j].write_v(cdata, w_obj, optvarsize) + j += 1 return optvarsize elif space.isinstance_w(w_ob, space.w_dict): @@ -185,14 +187,6 @@ class W_CTypeUnion(W_CTypeStructOrUnion): kind = "union" - def _check_only_one_argument_for_union(self, w_ob): - space = self.space - n = space.int_w(space.len(w_ob)) - if n > 1: - raise oefmt(space.w_ValueError, - "initializer for '%s': %d items given, but only one " - "supported (use a dict if needed)", self.name, n) - class W_CField(W_Root): _immutable_ = True @@ -200,18 +194,21 @@ BS_REGULAR = -1 BS_EMPTY_ARRAY = -2 - def __init__(self, ctype, offset, bitshift, bitsize): + BF_IGNORE_IN_CTOR = 0x01 + + def __init__(self, ctype, offset, bitshift, bitsize, flags): self.ctype = ctype self.offset = offset self.bitshift = bitshift # >= 0: bitshift; or BS_REGULAR/BS_EMPTY_ARRAY self.bitsize = bitsize + self.flags = flags # BF_xxx def is_bitfield(self): return self.bitshift >= 0 - def make_shifted(self, offset): + def make_shifted(self, offset, fflags): return W_CField(self.ctype, offset + self.offset, - self.bitshift, self.bitsize) + self.bitshift, self.bitsize, self.flags | fflags) def read(self, cdata): cdata = rffi.ptradd(cdata, self.offset) @@ -341,5 +338,6 @@ offset = interp_attrproperty('offset', W_CField), bitshift = interp_attrproperty('bitshift', W_CField), bitsize = interp_attrproperty('bitsize', W_CField), + flags = interp_attrproperty('flags', W_CField), ) W_CField.typedef.acceptable_as_base_class = False diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py --- a/pypy/module/_cffi_backend/newtype.py +++ b/pypy/module/_cffi_backend/newtype.py @@ -345,6 +345,11 @@ if alignment < falign and do_align: alignment = falign # + if is_union and i > 0: + fflags = ctypestruct.W_CField.BF_IGNORE_IN_CTOR + else: + fflags = 0 + # if fbitsize < 0: # not a bitfield: common case @@ -372,7 +377,7 @@ for name, srcfld in ftype._fields_dict.items(): srcfield2names[srcfld] = name for srcfld in ftype._fields_list: - fld = srcfld.make_shifted(boffset // 8) + fld = srcfld.make_shifted(boffset // 8, fflags) fields_list.append(fld) try: fields_dict[srcfield2names[srcfld]] = fld @@ -382,7 +387,8 @@ w_ctype._custom_field_pos = True else: # a regular field - fld = ctypestruct.W_CField(ftype, boffset // 8, bs_flag, -1) + fld = ctypestruct.W_CField(ftype, boffset // 8, bs_flag, -1, + fflags) fields_list.append(fld) fields_dict[fname] = fld @@ -489,7 +495,7 @@ bitshift = 8 * ftype.size - fbitsize- bitshift fld = ctypestruct.W_CField(ftype, field_offset_bytes, - bitshift, fbitsize) + bitshift, fbitsize, fflags) fields_list.append(fld) fields_dict[fname] = fld 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 @@ -1,7 +1,7 @@ # ____________________________________________________________ import sys -assert __version__ == "1.8.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.8.2", ("This test_c.py file is for testing a version" " of cffi that differs from the one that we" " get from 'import _cffi_backend'") if sys.version_info < (3,): @@ -2525,6 +2525,25 @@ assert d[2][1].bitshift == -1 assert d[2][1].bitsize == -1 +def test_nested_anonymous_struct_2(): + BInt = new_primitive_type("int") + BStruct = new_struct_type("struct foo") + BInnerUnion = new_union_type("union bar") + complete_struct_or_union(BInnerUnion, [('a1', BInt, -1), + ('a2', BInt, -1)]) + complete_struct_or_union(BStruct, [('b1', BInt, -1), + ('', BInnerUnion, -1), + ('b2', BInt, -1)]) + assert sizeof(BInnerUnion) == sizeof(BInt) + assert sizeof(BStruct) == sizeof(BInt) * 3 + fields = [(name, fld.offset, fld.flags) for (name, fld) in BStruct.fields] + assert fields == [ + ('b1', 0 * sizeof(BInt), 0), + ('a1', 1 * sizeof(BInt), 0), + ('a2', 1 * sizeof(BInt), 1), + ('b2', 2 * sizeof(BInt), 0), + ] + def test_sizeof_union(): # a union has the largest alignment of its members, and a total size # that is the largest of its items *possibly further aligned* if diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py --- a/pypy/module/cpyext/api.py +++ b/pypy/module/cpyext/api.py @@ -121,7 +121,7 @@ METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O Py_TPFLAGS_HAVE_INPLACEOPS Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_HAVE_NEWBUFFER -Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES +Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES Py_MAX_NDIMS Py_CLEANUP_SUPPORTED """.split() for name in constant_names: @@ -647,6 +647,9 @@ ('format', rffi.CCHARP), ('shape', Py_ssize_tP), ('strides', Py_ssize_tP), + ('_format', rffi.UCHAR), + ('_shape', rffi.CFixedArray(Py_ssize_t, Py_MAX_NDIMS)), + ('_strides', rffi.CFixedArray(Py_ssize_t, Py_MAX_NDIMS)), ('suboffsets', Py_ssize_tP), #('smalltable', rffi.CFixedArray(Py_ssize_t, 2)), ('internal', rffi.VOIDP) diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py --- a/pypy/module/cpyext/buffer.py +++ b/pypy/module/cpyext/buffer.py @@ -1,20 +1,66 @@ from pypy.interpreter.error import oefmt from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import buffer +from rpython.rlib.rarithmetic import widen from pypy.module.cpyext.api import ( cpython_api, CANNOT_FAIL, Py_buffer) from pypy.module.cpyext.pyobject import PyObject, Py_DecRef -# PyObject_GetBuffer has been removed, it is defined in abstract.c -# PyObject_CheckBuffer is also already defined +def _IsFortranContiguous(view): + ndim = widen(view.c_ndim) + if ndim == 0: + return 1 + if not view.c_strides: + return ndim == 1 + sd = view.c_itemsize + if ndim == 1: + return view.c_shape[0] == 1 or sd == view.c_strides[0] + for i in range(view.c_ndim): + dim = view.c_shape[i] + if dim == 0: + return 1 + if view.c_strides[i] != sd: + return 0 + sd *= dim + return 1 + +def _IsCContiguous(view): + ndim = widen(view.c_ndim) + if ndim == 0: + return 1 + if not view.c_strides: + return ndim == 1 + sd = view.c_itemsize + if ndim == 1: + return view.c_shape[0] == 1 or sd == view.c_strides[0] + for i in range(ndim - 1, -1, -1): + dim = view.c_shape[i] + if dim == 0: + return 1 + if view.c_strides[i] != sd: + return 0 + sd *= dim + return 1 + @cpython_api([lltype.Ptr(Py_buffer), lltype.Char], rffi.INT_real, error=CANNOT_FAIL) -def PyBuffer_IsContiguous(space, view, fortran): +def PyBuffer_IsContiguous(space, view, fort): """Return 1 if the memory defined by the view is C-style (fortran is 'C') or Fortran-style (fortran is 'F') contiguous or either one (fortran is 'A'). Return 0 otherwise.""" - # PyPy only supports contiguous Py_buffers for now. - return 1 + # traverse the strides, checking for consistent stride increases from + # right-to-left (c) or left-to-right (fortran). Copied from cpython + if not view.c_suboffsets: + return 0 + if (fort == 'C'): + return _IsCContiguous(view) + elif (fort == 'F'): + return _IsFortranContiguous(view) + elif (fort == 'A'): + return (_IsCContiguous(view) or _IsFortranContiguous(view)) + return 0 + + class CBuffer(buffer.Buffer): diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py --- a/pypy/module/cpyext/bytesobject.py +++ b/pypy/module/cpyext/bytesobject.py @@ -14,45 +14,33 @@ ## Implementation of PyBytesObject ## ================================ ## -## The problem -## ----------- +## PyBytesObject has its own ob_sval buffer, so we have two copies of a string; +## one in the PyBytesObject returned from various C-API functions and another +## in the corresponding RPython object. ## -## PyBytes_AsString() must return a (non-movable) pointer to the underlying -## ob_sval, whereas pypy strings are movable. C code may temporarily store -## this address and use it, as long as it owns a reference to the PyObject. -## There is no "release" function to specify that the pointer is not needed -## any more. +## The following calls can create a PyBytesObject without a correspoinding +## RPython object: ## -## Also, the pointer may be used to fill the initial value of string. This is -## valid only when the string was just allocated, and is not used elsewhere. +## PyBytes_FromStringAndSize(NULL, n) / PyString_FromStringAndSize(NULL, n) ## -## Solution -## -------- +## In the PyBytesObject returned, the ob_sval buffer may be modified as +## long as the freshly allocated PyBytesObject is not "forced" via a call +## to any of the more sophisticated C-API functions. ## -## PyBytesObject contains two additional members: the ob_size and a pointer to a -## char ob_sval; it may be NULL. -## -## - A string allocated by pypy will be converted into a PyBytesObject with a -## NULL buffer. The first time PyBytes_AsString() is called, memory is -## allocated (with flavor='raw') and content is copied. -## -## - A string allocated with PyBytes_FromStringAndSize(NULL, size) will -## allocate a PyBytesObject structure, and a buffer with the specified -## size+1, but the reference won't be stored in the global map; there is no -## corresponding object in pypy. When from_ref() or Py_INCREF() is called, -## the pypy string is created, and added to the global map of tracked -## objects. The buffer is then supposed to be immutable. -## -##- A buffer obtained from PyBytes_AS_STRING() could be mutable iff -## there is no corresponding pypy object for the string -## -## - _PyBytes_Resize() works only on not-yet-pypy'd strings, and returns a -## similar object. -## -## - PyBytes_Size() doesn't need to force the object. +## Care has been taken in implementing the functions below, so that +## if they are called with a non-forced PyBytesObject, they will not +## unintentionally force the creation of a RPython object. As long as only these +## are used, the ob_sval buffer is still modifiable: +## +## PyBytes_AsString / PyString_AsString +## PyBytes_AS_STRING / PyString_AS_STRING +## PyBytes_AsStringAndSize / PyString_AsStringAndSize +## PyBytes_Size / PyString_Size +## PyBytes_Resize / PyString_Resize +## _PyBytes_Resize / _PyString_Resize (raises if called with a forced object) ## ## - There could be an (expensive!) check in from_ref() that the buffer still -## corresponds to the pypy gc-managed string. +## corresponds to the pypy gc-managed string, ## PyBytesObjectStruct = lltype.ForwardReference() @@ -150,9 +138,6 @@ raise oefmt(space.w_TypeError, "expected bytes, %T found", from_ref(space, ref)) ref_str = rffi.cast(PyBytesObject, ref) - if not pyobj_has_w_obj(ref): - # XXX Force the ref? - bytes_realize(space, ref) return ref_str.c_ob_sval @cpython_api([rffi.VOIDP], rffi.CCHARP, error=0) @@ -170,9 +155,6 @@ if not PyBytes_Check(space, ref): raise oefmt(space.w_TypeError, "expected bytes, %T found", from_ref(space, ref)) - if not pyobj_has_w_obj(ref): - # force the ref - bytes_realize(space, ref) ref_str = rffi.cast(PyBytesObject, ref) data[0] = ref_str.c_ob_sval if length: diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h --- a/pypy/module/cpyext/include/object.h +++ b/pypy/module/cpyext/include/object.h @@ -131,7 +131,8 @@ typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *); -/* Py3k buffer interface */ +/* Py3k buffer interface, adapted for PyPy */ +#define Py_MAX_NDIMS 32 typedef struct bufferinfo { void *buf; PyObject *obj; /* owned reference */ @@ -145,12 +146,14 @@ char *format; Py_ssize_t *shape; Py_ssize_t *strides; - Py_ssize_t *suboffsets; - + Py_ssize_t *suboffsets; /* alway NULL for app-level objects*/ + unsigned char _format; + Py_ssize_t _strides[Py_MAX_NDIMS]; + Py_ssize_t _shape[Py_MAX_NDIMS]; /* static store for shape and strides of mono-dimensional buffers. */ /* Py_ssize_t smalltable[2]; */ - void *internal; + void *internal; /* always NULL for app-level objects */ } Py_buffer; diff --git a/pypy/module/cpyext/include/patchlevel.h b/pypy/module/cpyext/include/patchlevel.h --- a/pypy/module/cpyext/include/patchlevel.h +++ b/pypy/module/cpyext/include/patchlevel.h @@ -29,8 +29,8 @@ #define PY_VERSION "3.3.5" /* PyPy version as a string */ -#define PYPY_VERSION "5.4.1-alpha0" -#define PYPY_VERSION_NUM 0x05040100 +#define PYPY_VERSION "5.5.0-alpha0" +#define PYPY_VERSION_NUM 0x05050000 /* Defined to mean a PyPy where cpyext holds more regular references to PyObjects, e.g. staying alive as long as the internal PyPy object diff --git a/pypy/module/cpyext/memoryobject.py b/pypy/module/cpyext/memoryobject.py --- a/pypy/module/cpyext/memoryobject.py +++ b/pypy/module/cpyext/memoryobject.py @@ -1,7 +1,8 @@ from pypy.module.cpyext.api import (cpython_api, Py_buffer, CANNOT_FAIL, - build_type_checkers) -from pypy.module.cpyext.pyobject import PyObject -from rpython.rtyper.lltypesystem import lltype + Py_MAX_NDIMS, build_type_checkers, Py_ssize_tP) +from pypy.module.cpyext.pyobject import PyObject, make_ref, incref +from rpython.rtyper.lltypesystem import lltype, rffi +from pypy.objspace.std.memoryobject import W_MemoryView from pypy.interpreter.error import oefmt from pypy.module.cpyext.pyobject import PyObject, from_ref @@ -16,6 +17,7 @@ @cpython_api([PyObject], PyObject) def PyMemoryView_GET_BASE(space, w_obj): # return the obj field of the Py_buffer created by PyMemoryView_GET_BUFFER + # XXX needed for numpy on py3k raise NotImplementedError('PyMemoryView_GET_BUFFER') @cpython_api([PyObject], lltype.Ptr(Py_buffer), error=CANNOT_FAIL) @@ -24,24 +26,38 @@ object. The object must be a memoryview instance; this macro doesn't check its type, you must do it yourself or you will risk crashes.""" view = lltype.malloc(Py_buffer, flavor='raw', zero=True) - # TODO - fill in fields - ''' - view.c_buf = buf - view.c_len = length - view.c_obj = obj - Py_IncRef(space, obj) - view.c_itemsize = 1 - rffi.setintfield(view, 'c_readonly', readonly) - rffi.setintfield(view, 'c_ndim', 0) - view.c_format = lltype.nullptr(rffi.CCHARP.TO) - view.c_shape = lltype.nullptr(Py_ssize_tP.TO) - view.c_strides = lltype.nullptr(Py_ssize_tP.TO) + if not isinstance(w_obj, W_MemoryView): + return view + ndim = w_obj.buf.getndim() + if ndim >= Py_MAX_NDIMS: + # XXX warn? + return view + try: + view.c_buf = rffi.cast(rffi.VOIDP, w_obj.buf.get_raw_address()) + view.c_obj = make_ref(space, w_obj) + rffi.setintfield(view, 'c_readonly', w_obj.buf.readonly) + isstr = False + except ValueError: + w_s = w_obj.descr_tobytes(space) + view.c_obj = make_ref(space, w_s) + rffi.setintfield(view, 'c_readonly', 1) + isstr = True + view.c_len = w_obj.getlength() + view.c_itemsize = w_obj.buf.getitemsize() + rffi.setintfield(view, 'c_ndim', ndim) + view.c__format = rffi.cast(rffi.UCHAR, w_obj.buf.getformat()) + view.c_format = rffi.cast(rffi.CCHARP, view.c__format) + view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape) + view.c_strides = rffi.cast(Py_ssize_tP, view.c__strides) + shape = w_obj.buf.getshape() + strides = w_obj.buf.getstrides() + for i in range(ndim): + view.c_shape[i] = shape[i] + view.c_strides[i] = strides[i] view.c_suboffsets = lltype.nullptr(Py_ssize_tP.TO) view.c_internal = lltype.nullptr(rffi.VOIDP.TO) - ''' return view - @cpython_api([lltype.Ptr(Py_buffer)], PyObject) def PyMemoryView_FromBuffer(space, view): """Create a memoryview object wrapping the given buffer structure view. diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py --- a/pypy/module/cpyext/slotdefs.py +++ b/pypy/module/cpyext/slotdefs.py @@ -335,9 +335,15 @@ def getshape(self): return self.shape + def getstrides(self): + return self.strides + def getitemsize(self): return self.itemsize + def getndim(self): + return self.ndim + def wrap_getbuffer(space, w_self, w_args, func): func_target = rffi.cast(getbufferproc, func) with lltype.scoped_alloc(Py_buffer) as pybuf: diff --git a/pypy/module/cpyext/test/buffer_test.c b/pypy/module/cpyext/test/buffer_test.c --- a/pypy/module/cpyext/test/buffer_test.c +++ b/pypy/module/cpyext/test/buffer_test.c @@ -107,14 +107,11 @@ PyMyArray_getbuffer(PyObject *obj, Py_buffer *view, int flags) { PyMyArray* self = (PyMyArray*)obj; - fprintf(stdout, "in PyMyArray_getbuffer\n"); if (view == NULL) { - fprintf(stdout, "view is NULL\n"); PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer"); return -1; } if (flags == 0) { - fprintf(stdout, "flags is 0\n"); PyErr_SetString(PyExc_ValueError, "flags == 0 in getbuffer"); return -1; } @@ -188,7 +185,131 @@ (initproc)PyMyArray_init, /* tp_init */ }; +static PyObject* +test_buffer(PyObject* self, PyObject* args) +{ + Py_buffer* view = NULL; + PyObject* obj = PyTuple_GetItem(args, 0); + PyObject* memoryview = PyMemoryView_FromObject(obj); + if (memoryview == NULL) + return PyInt_FromLong(-1); + view = PyMemoryView_GET_BUFFER(memoryview); + Py_DECREF(memoryview); + return PyInt_FromLong(view->len); +} + +/* Copied from numpy tests */ +/* + * Create python string from a FLAG and or the corresponding PyBuf flag + * for the use in get_buffer_info. + */ +#define GET_PYBUF_FLAG(FLAG) \ + buf_flag = PyUnicode_FromString(#FLAG); \ + flag_matches = PyObject_RichCompareBool(buf_flag, tmp, Py_EQ); \ + Py_DECREF(buf_flag); \ + if (flag_matches == 1) { \ + Py_DECREF(tmp); \ + flags |= PyBUF_##FLAG; \ + continue; \ + } \ + else if (flag_matches == -1) { \ + Py_DECREF(tmp); \ + return NULL; \ + } + + +/* + * Get information for a buffer through PyBuf_GetBuffer with the + * corresponding flags or'ed. Note that the python caller has to + * make sure that or'ing those flags actually makes sense. + * More information should probably be returned for future tests. + */ +static PyObject * +get_buffer_info(PyObject *self, PyObject *args) +{ + PyObject *buffer_obj, *pyflags; + PyObject *tmp, *buf_flag; + Py_buffer buffer; + PyObject *shape, *strides; + Py_ssize_t i, n; + int flag_matches; + int flags = 0; + + if (!PyArg_ParseTuple(args, "OO", &buffer_obj, &pyflags)) { + return NULL; + } + + n = PySequence_Length(pyflags); + if (n < 0) { + return NULL; + } + + for (i=0; i < n; i++) { + tmp = PySequence_GetItem(pyflags, i); + if (tmp == NULL) { + return NULL; + } + + GET_PYBUF_FLAG(SIMPLE); + GET_PYBUF_FLAG(WRITABLE); + GET_PYBUF_FLAG(STRIDES); + GET_PYBUF_FLAG(ND); + GET_PYBUF_FLAG(C_CONTIGUOUS); + GET_PYBUF_FLAG(F_CONTIGUOUS); + GET_PYBUF_FLAG(ANY_CONTIGUOUS); + GET_PYBUF_FLAG(INDIRECT); + GET_PYBUF_FLAG(FORMAT); + GET_PYBUF_FLAG(STRIDED); + GET_PYBUF_FLAG(STRIDED_RO); + GET_PYBUF_FLAG(RECORDS); + GET_PYBUF_FLAG(RECORDS_RO); + GET_PYBUF_FLAG(FULL); + GET_PYBUF_FLAG(FULL_RO); + GET_PYBUF_FLAG(CONTIG); + GET_PYBUF_FLAG(CONTIG_RO); + + Py_DECREF(tmp); + + /* One of the flags must match */ + PyErr_SetString(PyExc_ValueError, "invalid flag used."); + return NULL; + } + + if (PyObject_GetBuffer(buffer_obj, &buffer, flags) < 0) { + return NULL; + } + + if (buffer.shape == NULL) { + Py_INCREF(Py_None); + shape = Py_None; + } + else { + shape = PyTuple_New(buffer.ndim); + for (i=0; i < buffer.ndim; i++) { + PyTuple_SET_ITEM(shape, i, PyLong_FromSsize_t(buffer.shape[i])); + } + } + + if (buffer.strides == NULL) { + Py_INCREF(Py_None); + strides = Py_None; + } + else { + strides = PyTuple_New(buffer.ndim); + for (i=0; i < buffer.ndim; i++) { + PyTuple_SET_ITEM(strides, i, PyLong_FromSsize_t(buffer.strides[i])); + } + } + + PyBuffer_Release(&buffer); + return Py_BuildValue("(NN)", shape, strides); +} + + + static PyMethodDef buffer_functions[] = { + {"test_buffer", (PyCFunction)test_buffer, METH_VARARGS, NULL}, + {"get_buffer_info", (PyCFunction)get_buffer_info, METH_VARARGS, NULL}, {NULL, NULL} /* Sentinel */ }; diff --git a/pypy/module/cpyext/test/test_bytesobject.py b/pypy/module/cpyext/test/test_bytesobject.py --- a/pypy/module/cpyext/test/test_bytesobject.py +++ b/pypy/module/cpyext/test/test_bytesobject.py @@ -179,8 +179,27 @@ Py_INCREF(Py_None); return Py_None; """), + ("c_only", "METH_NOARGS", + """ + int ret; + char * buf2; + PyObject * obj = PyBytes_FromStringAndSize(NULL, 1024); + if (!obj) + return NULL; + buf2 = PyBytes_AsString(obj); + if (!buf2) + return NULL; + /* buf should not have been forced, issue #2395 */ + ret = _PyBytes_Resize(&obj, 512); + if (ret < 0) + return NULL; + Py_DECREF(obj); + Py_INCREF(Py_None); + return Py_None; + """), ]) module.getbytes() + module.c_only() class TestBytes(BaseApiTest): diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -1,6 +1,6 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase - +from rpython.rlib.buffer import StringBuffer class TestMemoryViewObject(BaseApiTest): def test_fromobject(self, space, api): w_hello = space.newbytes("hello") @@ -11,6 +11,13 @@ w_bytes = space.call_method(w_view, "tobytes") assert space.unwrap(w_bytes) == "hello" + def test_frombuffer(self, space, api): + w_buf = space.newbuffer(StringBuffer("hello")) + w_memoryview = api.PyMemoryView_FromObject(w_buf) + w_view = api.PyMemoryView_GET_BUFFER(w_memoryview) + ndim = w_view.c_ndim + assert ndim == 1 + class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): def test_fillWithObject(self): module = self.import_extension('foo', [ @@ -62,6 +69,25 @@ y = memoryview(arr) assert y.format == 'i' assert y.shape == (10,) + assert len(y) == 10 s = y[3] assert len(s) == struct.calcsize('i') assert s == struct.pack('i', 3) + viewlen = module.test_buffer(arr) + assert viewlen == y.itemsize * len(y) + + def test_buffer_info(self): + from _numpypy import multiarray as np + module = self.import_module(name='buffer_test') + get_buffer_info = module.get_buffer_info + # test_export_flags from numpy test_multiarray + raises(ValueError, get_buffer_info, np.arange(5)[::2], ('SIMPLE',)) + # test_relaxed_strides from numpy test_multiarray + arr = np.zeros((1, 10)) + if arr.flags.f_contiguous: + shape, strides = get_buffer_info(arr, ['F_CONTIGUOUS']) + assert strides[0] == 8 + arr = np.ones((10, 1), order='F') + shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS']) + assert strides[-1] == 8 + diff --git a/pypy/module/cpyext/test/test_version.py b/pypy/module/cpyext/test/test_version.py --- a/pypy/module/cpyext/test/test_version.py +++ b/pypy/module/cpyext/test/test_version.py @@ -41,9 +41,11 @@ assert module.py_minor_version == sys.version_info.minor assert module.py_micro_version == sys.version_info.micro - @pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='pypy only test') + #@pytest.mark.skipif('__pypy__' not in sys.builtin_module_names, reason='pypy only test') def test_pypy_versions(self): import sys + if '__pypy__' not in sys.builtin_module_names: + py.test.skip("pypy only test") init = """ if (Py_IsInitialized()) { PyObject *m = Py_InitModule("foo", NULL); diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -293,6 +293,8 @@ STRUCT_TYPE = PyNumberMethods elif slot_names[0] == 'c_tp_as_sequence': STRUCT_TYPE = PySequenceMethods + elif slot_names[0] == 'c_tp_as_buffer': + STRUCT_TYPE = PyBufferProcs else: raise AssertionError( "Structure not allocated: %s" % (slot_names[0],)) diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py --- a/pypy/module/micronumpy/compile.py +++ b/pypy/module/micronumpy/compile.py @@ -460,6 +460,9 @@ def getdictvalue(self, space, key): return self.items[key] + def descr_memoryview(self, space, buf): + raise oefmt(space.w_TypeError, "error") + class IterDictObject(W_Root): def __init__(self, space, w_dict): self.space = space diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py --- a/pypy/module/micronumpy/concrete.py +++ b/pypy/module/micronumpy/concrete.py @@ -377,7 +377,25 @@ def __exit__(self, typ, value, traceback): keepalive_until_here(self) - def get_buffer(self, space, readonly): + def get_buffer(self, space, flags): + errtype = space.w_ValueError # should be BufferError, numpy does this instead + if ((flags & space.BUF_C_CONTIGUOUS) == space.BUF_C_CONTIGUOUS and + not self.flags & NPY.ARRAY_C_CONTIGUOUS): + raise oefmt(errtype, "ndarray is not C-contiguous") + if ((flags & space.BUF_F_CONTIGUOUS) == space.BUF_F_CONTIGUOUS and + not self.flags & NPY.ARRAY_F_CONTIGUOUS): + raise oefmt(errtype, "ndarray is not Fortran contiguous") + if ((flags & space.BUF_ANY_CONTIGUOUS) == space.BUF_ANY_CONTIGUOUS and + not (self.flags & NPY.ARRAY_F_CONTIGUOUS and + self.flags & NPY.ARRAY_C_CONTIGUOUS)): + raise oefmt(errtype, "ndarray is not contiguous") + if ((flags & space.BUF_STRIDES) != space.BUF_STRIDES and + not self.flags & NPY.ARRAY_C_CONTIGUOUS): + raise oefmt(errtype, "ndarray is not C-contiguous") + if ((flags & space.BUF_WRITABLE) == space.BUF_WRITABLE and + not self.flags & NPY.ARRAY_WRITEABLE): + raise oefmt(errtype, "buffer source array is read-only") + readonly = not (flags & space.BUF_WRITABLE) == space.BUF_WRITABLE return ArrayBuffer(self, readonly) def astype(self, space, dtype, order, copy=True): @@ -695,6 +713,7 @@ index + self.impl.start) def setitem(self, index, v): + # XXX what if self.readonly? raw_storage_setitem(self.impl.storage, index + self.impl.start, rffi.cast(lltype.Char, v)) diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py --- a/pypy/module/micronumpy/ctors.py +++ b/pypy/module/micronumpy/ctors.py @@ -1,4 +1,5 @@ from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.baseobjspace import BufferInterfaceNotFound from pypy.interpreter.gateway import unwrap_spec, WrappedDefault from rpython.rlib.buffer import SubBuffer from rpython.rlib.rstring import strip_spaces @@ -42,7 +43,7 @@ raise oefmt(space.w_ValueError, "object __array__ method not producing an array") -def try_interface_method(space, w_object): +def try_interface_method(space, w_object, copy): try: w_interface = space.getattr(w_object, space.wrap("__array_interface__")) if w_interface is None: @@ -81,17 +82,20 @@ raise oefmt(space.w_ValueError, "__array_interface__ could not decode dtype %R", w_dtype ) - if w_data is not None and (space.isinstance_w(w_data, space.w_tuple) or space.isinstance_w(w_data, space.w_list)): + if w_data is not None and (space.isinstance_w(w_data, space.w_tuple) or + space.isinstance_w(w_data, space.w_list)): data_w = space.listview(w_data) - data = rffi.cast(RAW_STORAGE_PTR, space.int_w(data_w[0])) - read_only = True # XXX why not space.is_true(data_w[1]) + w_data = rffi.cast(RAW_STORAGE_PTR, space.int_w(data_w[0])) + read_only = space.is_true(data_w[1]) or copy offset = 0 - return W_NDimArray.from_shape_and_storage(space, shape, data, - dtype, strides=strides, start=offset), read_only + w_base = w_object + if read_only: + w_base = None + return W_NDimArray.from_shape_and_storage(space, shape, w_data, + dtype, w_base=w_base, strides=strides, + start=offset), read_only if w_data is None: - data = w_object - else: - data = w_data + w_data = w_object w_offset = space.finditem(w_interface, space.wrap('offset')) if w_offset is None: offset = 0 @@ -101,7 +105,7 @@ if strides is not None: raise oefmt(space.w_NotImplementedError, "__array_interface__ strides not fully supported yet") - arr = frombuffer(space, data, dtype, support.product(shape), offset) + arr = frombuffer(space, w_data, dtype, support.product(shape), offset) new_impl = arr.implementation.reshape(arr, shape) return W_NDimArray(new_impl), False @@ -110,6 +114,78 @@ return None, False raise +def _descriptor_from_pep3118_format(space, c_format): + descr = descriptor.decode_w_dtype(space, space.wrap(c_format)) + if descr: + return descr + msg = "invalid PEP 3118 format string: '%s'" % c_format + space.warn(space.wrap(msg), space.w_RuntimeWarning) + return None + +def _array_from_buffer_3118(space, w_object, dtype): + try: + w_buf = space.call_method(space.builtin, "memoryview", w_object) + except OperationError as e: + if e.match(space, space.w_TypeError): + # object does not have buffer interface + return w_object + raise + format = space.getattr(w_buf,space.newbytes('format')) + if format: + descr = _descriptor_from_pep3118_format(space, space.str_w(format)) + if not descr: + return w_object + if dtype and descr: + raise oefmt(space.w_NotImplementedError, + "creating an array from a memoryview while specifying dtype " + "not supported") + if descr.elsize != space.int_w(space.getattr(w_buf, space.newbytes('itemsize'))): + msg = ("Item size computed from the PEP 3118 buffer format " + "string does not match the actual item size.") + space.warn(space.wrap(msg), space.w_RuntimeWarning) + return w_object + dtype = descr + elif not dtype: + dtype = descriptor.get_dtype_cache(space).w_stringdtype + dtype.elsize = space.int_w(space.getattr(w_buf, space.newbytes('itemsize'))) + nd = space.int_w(space.getattr(w_buf, space.newbytes('ndim'))) + shape = [space.int_w(d) for d in space.listview( + space.getattr(w_buf, space.newbytes('shape')))] + strides = [] + buflen = space.len_w(w_buf) * dtype.elsize + if shape: + strides = [space.int_w(d) for d in space.listview( + space.getattr(w_buf, space.newbytes('strides')))] + if not strides: + d = buflen + strides = [0] * nd + for k in range(nd): + if shape[k] > 0: + d /= shape[k] + strides[k] = d + else: + if nd == 1: + shape = [buflen / dtype.elsize, ] + strides = [dtype.elsize, ] + elif nd > 1: + msg = ("ndim computed from the PEP 3118 buffer format " + "is greater than 1, but shape is NULL.") + space.warn(space.wrap(msg), space.w_RuntimeWarning) + return w_object + try: + w_data = rffi.cast(RAW_STORAGE_PTR, space.int_w(space.call_method(w_buf, '_pypy_raw_address'))) + except OperationError as e: + if e.match(space, space.w_ValueError): + return w_object + else: + raise e + writable = not space.bool_w(space.getattr(w_buf, space.newbytes('readonly'))) + w_ret = W_NDimArray.from_shape_and_storage(space, shape, w_data, + storage_bytes=buflen, dtype=dtype, w_base=w_object, + writable=writable, strides=strides) + if w_ret: + return w_ret + return w_object @unwrap_spec(ndmin=int, copy=bool, subok=bool) def array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False, @@ -127,6 +203,7 @@ def _array(space, w_object, w_dtype=None, copy=True, w_order=None, subok=False): + from pypy.module.micronumpy.boxes import W_GenericBox # numpy testing calls array(type(array([]))) and expects a ValueError if space.isinstance_w(w_object, space.w_type): raise oefmt(space.w_ValueError, "cannot create ndarray from type instance") @@ -134,13 +211,19 @@ dtype = descriptor.decode_w_dtype(space, w_dtype) if not isinstance(w_object, W_NDimArray): w_array = try_array_method(space, w_object, w_dtype) - if w_array is not None: + if w_array is None: + if ( not space.isinstance_w(w_object, space.w_str) and + not space.isinstance_w(w_object, space.w_unicode) and + not isinstance(w_object, W_GenericBox)): + # use buffer interface + w_object = _array_from_buffer_3118(space, w_object, dtype) + else: # continue with w_array, but do further operations in place w_object = w_array copy = False dtype = w_object.get_dtype() if not isinstance(w_object, W_NDimArray): - w_array, _copy = try_interface_method(space, w_object) + w_array, _copy = try_interface_method(space, w_object, copy) if w_array is not None: w_object = w_array copy = _copy diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -806,10 +806,10 @@ def buffer_w(self, space, flags): # XXX format isn't always 'B' probably - return self.implementation.get_buffer(space, True) + return self.implementation.get_buffer(space, flags) def descr_get_data(self, space): - return space.newbuffer(self.implementation.get_buffer(space, False)) + return space.newbuffer(self.implementation.get_buffer(space, space.BUF_FULL)) @unwrap_spec(offset=int, axis1=int, axis2=int) def descr_diagonal(self, space, offset=0, axis1=0, axis2=1): diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py --- a/pypy/module/micronumpy/test/test_ndarray.py +++ b/pypy/module/micronumpy/test/test_ndarray.py @@ -3206,7 +3206,9 @@ raises(TypeError, array, Dummy({'version': 3, 'typestr': 'f8', 'shape': ('a', 3)})) a = array([1, 2, 3]) - b = array(Dummy(a.__array_interface__)) + d = Dummy(a.__array_interface__) + b = array(d) + assert b.base is None b[1] = 200 assert a[1] == 2 # upstream compatibility, is this a bug? interface_a = a.__array_interface__ @@ -3217,6 +3219,8 @@ interface_b.pop('data') interface_a.pop('data') assert interface_a == interface_b + b = array(d, copy=False) + assert b.base is d b = array(Dummy({'version':3, 'shape': (50,), 'typestr': 'u1', 'data': 'a'*100})) @@ -3585,6 +3589,7 @@ cls.w_float32val = cls.space.wrap(struct.pack('f', 5.2)) cls.w_float64val = cls.space.wrap(struct.pack('d', 300.4)) cls.w_ulongval = cls.space.wrap(struct.pack('L', 12)) + cls.w_one = cls.space.wrap(struct.pack('i', 1)) def test_frombuffer(self): import numpy as np @@ -3636,8 +3641,6 @@ else: EMPTY = None x = np.array([1, 2, 3, 4, 5], dtype='i') - y = memoryview('abc') - assert y.format == 'B' y = memoryview(x) assert y.format == 'i' assert y.shape == (5,) @@ -3645,6 +3648,16 @@ assert y.strides == (4,) assert y.suboffsets == EMPTY assert y.itemsize == 4 + assert isinstance(y, memoryview) + assert y[0] == self.one + assert (np.array(y) == x).all() + + x = np.array([0, 0, 0, 0], dtype='O') + y = memoryview(x) + # handles conversion of address to pinned object? + z = np.array(y) + assert z.dtype == 'O' + assert (z == x).all() def test_fromstring(self): import sys diff --git a/pypy/module/micronumpy/test/test_subtype.py b/pypy/module/micronumpy/test/test_subtype.py --- a/pypy/module/micronumpy/test/test_subtype.py +++ b/pypy/module/micronumpy/test/test_subtype.py @@ -702,3 +702,32 @@ ret = obj.sum() print type(ret) assert ret.info == 'spam' + + def test_ndarray_subclass_assigns_base(self): + import numpy as np + init_called = [] + class _DummyArray(object): + """ Dummy object that just exists to hang __array_interface__ dictionaries + and possibly keep alive a reference to a base array. + """ + def __init__(self, interface, base=None): + self.__array_interface__ = interface + init_called.append(1) + self.base = base + + x = np.zeros(10) + d = _DummyArray(x.__array_interface__, base=x) + y = np.array(d, copy=False) + assert sum(init_called) == 1 + assert y.base is d + + x = np.zeros((0,), dtype='float32') + intf = x.__array_interface__.copy() + intf["strides"] = x.strides + x.__array_interface__["strides"] = x.strides + d = _DummyArray(x.__array_interface__, base=x) + y = np.array(d, copy=False) + assert sum(init_called) == 2 + assert y.base is d + + diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py --- a/pypy/module/micronumpy/types.py +++ b/pypy/module/micronumpy/types.py @@ -1851,7 +1851,7 @@ arr.gcstruct) def read(self, arr, i, offset, dtype): - if arr.gcstruct is V_OBJECTSTORE: + if arr.gcstruct is V_OBJECTSTORE and not arr.base(): raise oefmt(self.space.w_NotImplementedError, "cannot read object from array with no gc hook") return self.box(self._read(arr.storage, i, offset)) diff --git a/pypy/module/pypyjit/test_pypy_c/test_import.py b/pypy/module/pypyjit/test_pypy_c/test_import.py --- a/pypy/module/pypyjit/test_pypy_c/test_import.py +++ b/pypy/module/pypyjit/test_pypy_c/test_import.py @@ -38,3 +38,27 @@ # call_may_force(absolute_import_with_lock). for opname in log.opnames(loop.allops(opcode="IMPORT_NAME")): assert 'call' not in opname # no call-like opcode + + def test_import_fast_path(self, tmpdir): + print tmpdir + pkg = tmpdir.join('mypkg').ensure(dir=True) + subdir = pkg.join("sub").ensure(dir=True) + pkg.join('__init__.py').write("") + subdir.join('__init__.py').write("") + subdir.join('mod.py').write(str(py.code.Source(""" + def do_the_import(): + import sys + """))) + def main(path, n): + def do_the_import(): + from mypkg.sub import mod + import sys + sys.path.append(path) + for i in range(n): + do_the_import() + # + log = self.run(main, [str(tmpdir), 300]) + loop, = log.loops_by_filename(self.filepath) + # check that no string compares and other calls are there + for opname in log.opnames(loop.allops(opcode="IMPORT_NAME")): + assert 'call' not in opname # no call-like opcode diff --git a/pypy/module/pypyjit/test_pypy_c/test_instance.py b/pypy/module/pypyjit/test_pypy_c/test_instance.py --- a/pypy/module/pypyjit/test_pypy_c/test_instance.py +++ b/pypy/module/pypyjit/test_pypy_c/test_instance.py @@ -187,6 +187,43 @@ """) + def test_oldstyle_methcall(self): + def main(): + def g(): pass + class A: + def f(self): + return self.x + 1 + class I(A): + pass + class J(I): + pass + + + class B(J): + def __init__(self, x): + self.x = x + + i = 0 + b = B(1) + while i < 1000: + g() + v = b.f() # ID: meth + i += v + return i + + log = self.run(main, [], threshold=80) + loop, = log.loops_by_filename(self.filepath, is_entry_bridge=True) + assert loop.match_by_id('meth', + ''' + guard_nonnull_class(p18, ..., descr=...) + p52 = getfield_gc_r(p18, descr=...) # read map + guard_value(p52, ConstPtr(ptr53), descr=...) + p54 = getfield_gc_r(p18, descr=...) # read class + guard_value(p54, ConstPtr(ptr55), descr=...) + p56 = force_token() # done + ''') + + def test_oldstyle_newstyle_mix(self): def main(): class A: diff --git a/pypy/module/sys/version.py b/pypy/module/sys/version.py --- a/pypy/module/sys/version.py +++ b/pypy/module/sys/version.py @@ -10,7 +10,7 @@ #XXX # sync CPYTHON_VERSION with patchlevel.h, package.py CPYTHON_API_VERSION = 1013 #XXX # sync with include/modsupport.h -PYPY_VERSION = (5, 4, 1, "alpha", 0) #XXX # sync patchlevel.h +PYPY_VERSION = (5, 5, 0, "alpha", 0) #XXX # sync patchlevel.h import pypy diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py @@ -1415,6 +1415,7 @@ assert p.b == 12 assert p.c == 14 assert p.d == 14 + py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0]) def test_nested_field_offset_align(self): ffi = FFI(backend=self.Backend()) @@ -1454,14 +1455,42 @@ assert p.b == 0 assert p.c == 14 assert p.d == 14 - p = ffi.new("union foo_u *", {'b': 12}) - assert p.a == 0 + p = ffi.new("union foo_u *", {'a': -63, 'b': 12}) + assert p.a == -63 assert p.b == 12 - assert p.c == 0 - assert p.d == 0 - # we cannot specify several items in the dict, even though - # in theory in this particular case it would make sense - # to give both 'a' and 'b' + assert p.c == -63 + assert p.d == -63 + p = ffi.new("union foo_u *", [123, 456]) + assert p.a == 123 + assert p.b == 456 + assert p.c == 123 + assert p.d == 123 + py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0]) + + def test_nested_anonymous_struct_2(self): + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + struct foo_s { + int a; + union { int b; union { int c, d; }; }; + int e; + }; + """) + assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT + p = ffi.new("struct foo_s *", [11, 22, 33]) + assert p.a == 11 + assert p.b == p.c == p.d == 22 + assert p.e == 33 + py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44]) + FOO = ffi.typeof("struct foo_s") + fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields] + assert fields == [ + ('a', 0 * SIZE_OF_INT, 0), + ('b', 1 * SIZE_OF_INT, 0), + ('c', 1 * SIZE_OF_INT, 1), + ('d', 1 * SIZE_OF_INT, 1), + ('e', 2 * SIZE_OF_INT, 0), + ] def test_cast_to_array_type(self): ffi = FFI(backend=self.Backend()) @@ -1479,6 +1508,7 @@ assert p1[0] == 123 seen.append(1) q = ffi.gc(p, destructor) + assert ffi.typeof(q) is ffi.typeof(p) import gc; gc.collect() assert seen == [] del q diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ctypes.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ctypes.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ctypes.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_ctypes.py @@ -35,6 +35,9 @@ def test_nested_anonymous_union(self): py.test.skip("ctypes backend: not supported: nested anonymous union") + def test_nested_anonymous_struct_2(self): + py.test.skip("ctypes backend: not supported: nested anonymous union") + def test_CData_CType_2(self): if sys.version_info >= (3,): py.test.skip("ctypes backend: not supported in Python 3: CType") diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_zintegration.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_zintegration.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_zintegration.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_zintegration.py @@ -149,3 +149,28 @@ p = snip_setuptools_verify2.C.getpwuid(0) assert snip_setuptools_verify2.ffi.string(p.pw_name) == b"root" ''') + + def test_set_py_limited_api(self): + from cffi.setuptools_ext import _set_py_limited_api + try: + import setuptools + except ImportError as e: + py.test.skip(str(e)) + orig_version = setuptools.__version__ + try: + setuptools.__version__ = '26.0.0' + from setuptools import Extension + + kwds = _set_py_limited_api(Extension, {}) + assert kwds['py_limited_api'] == True + + setuptools.__version__ = '25.0' + kwds = _set_py_limited_api(Extension, {}) + assert not kwds + + setuptools.__version__ = 'development' + kwds = _set_py_limited_api(Extension, {}) + assert kwds['py_limited_api'] == True + + finally: + setuptools.__version__ = orig_version diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py @@ -1975,9 +1975,9 @@ def test_function_returns_partial_struct(): ffi = FFI() - ffi.cdef("struct a { int a; ...; }; struct a f1(int);") + ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") lib = verify(ffi, "test_function_returns_partial_struct", """ - struct a { int b, a, c; }; - static struct a f1(int x) { struct a s = {0}; s.a = x; return s; } + struct aaa { int b, a, c; }; + static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } """) assert lib.f1(52).a == 52 diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py b/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_numbers.py @@ -194,6 +194,29 @@ _fields_ = [('t', enum)] assert isinstance(S().t, enum) + def test_no_missing_shape_to_ffi_type(self): + # whitebox test + import sys + if '__pypy__' not in sys.builtin_module_names: + skip("only for pypy's ctypes") + skip("re-enable after adding 'g' to _shape_to_ffi_type.typemap, " + "which I think needs fighting all the way up from " + "rpython.rlib.libffi") + from _ctypes.basics import _shape_to_ffi_type + from _rawffi import Array + for i in range(1, 256): + try: + Array(chr(i)) + except ValueError: + pass + else: + assert chr(i) in _shape_to_ffi_type.typemap + + @py.test.mark.xfail + def test_pointer_to_long_double(self): + import ctypes + ctypes.POINTER(ctypes.c_longdouble) + ## def test_perf(self): ## check_perf() diff --git a/pypy/objspace/std/celldict.py b/pypy/objspace/std/celldict.py --- a/pypy/objspace/std/celldict.py +++ b/pypy/objspace/std/celldict.py @@ -64,6 +64,8 @@ def setitem_str(self, w_dict, key, w_value): cell = self.getdictvalue_no_unwrapping(w_dict, key) + #if (key == '__package__' or key == "__path__") and cell is not None and w_value is not cell: + # print "WARNING", key, w_value, cell, self return self._setitem_str_cell_known(cell, w_dict, key, w_value) def _setitem_str_cell_known(self, cell, w_dict, key, w_value): diff --git a/pypy/objspace/std/test/test_random_attr.py b/pypy/objspace/std/test/test_random_attr.py --- a/pypy/objspace/std/test/test_random_attr.py +++ b/pypy/objspace/std/test/test_random_attr.py @@ -1,4 +1,5 @@ import pytest +pytest.skip("This cannot possibly work on pypy3") import sys try: import __pypy__ diff --git a/pypy/tool/release/repackage.sh b/pypy/tool/release/repackage.sh --- a/pypy/tool/release/repackage.sh +++ b/pypy/tool/release/repackage.sh @@ -1,7 +1,7 @@ # Edit these appropriately before running this script maj=5 min=4 -rev=0 +rev=1 branchname=release-$maj.x # ==OR== release-$maj.$min.x tagname=release-pypy2.7-v$maj.$min.$rev # ==OR== release-$maj.$min diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py --- a/rpython/annotator/annrpython.py +++ b/rpython/annotator/annrpython.py @@ -164,8 +164,15 @@ # annotations that are passed in, and don't annotate the old # graph -- it's already low-level operations! for a, s_newarg in zip(block.inputargs, cells): - s_oldarg = self.binding(a) - assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg + s_oldarg = a.annotation + # XXX: Should use s_oldarg.contains(s_newarg) but that breaks + # PyPy translation + if annmodel.unionof(s_oldarg, s_newarg) != s_oldarg: + raise annmodel.AnnotatorError( + "Late-stage annotation is not allowed to modify the " + "existing annotation for variable %s: %s" % + (a, s_oldarg)) + else: assert not self.frozen if block not in self.annotated: diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py --- a/rpython/annotator/binaryop.py +++ b/rpython/annotator/binaryop.py @@ -17,7 +17,7 @@ from rpython.flowspace.model import Variable, Constant, const from rpython.flowspace.operation import op from rpython.rlib import rarithmetic -from rpython.annotator.model import AnnotatorError +from rpython.annotator.model import AnnotatorError, TLS BINARY_OPERATIONS = set([oper.opname for oper in op.__dict__.values() if oper.dispatch == 2]) @@ -436,6 +436,11 @@ class __extend__(pairtype(SomeFloat, SomeFloat)): def union((flt1, flt2)): + if not TLS.allow_int_to_float: + # in this mode, if one of the two is actually the + # subclass SomeInteger, complain + if isinstance(flt1, SomeInteger) or isinstance(flt2, SomeInteger): + raise UnionError(flt1, flt2) return SomeFloat() add = sub = mul = union diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py --- a/rpython/annotator/model.py +++ b/rpython/annotator/model.py @@ -44,6 +44,7 @@ # A global attribute :-( Patch it with 'True' to enable checking of # the no_nul attribute... check_str_without_nul = False + allow_int_to_float = True TLS = State() class SomeObject(object): @@ -749,6 +750,7 @@ s1 = pair(s1, s2).union() else: # this is just a performance shortcut + # XXX: This is a lie! Grep for no_side_effects_in_union and weep. if s1 != s2: s1 = pair(s1, s2).union() return s1 diff --git a/rpython/rlib/libffi.py b/rpython/rlib/libffi.py --- a/rpython/rlib/libffi.py +++ b/rpython/rlib/libffi.py @@ -47,6 +47,8 @@ cls.ulonglong = clibffi.cast_type_to_ffitype(rffi.ULONGLONG) cls.signed = clibffi.cast_type_to_ffitype(rffi.SIGNED) cls.wchar_t = clibffi.cast_type_to_ffitype(lltype.UniChar) + # XXX long double support: clibffi.ffi_type_longdouble, but then + # XXX fix the whole rest of this file to add a case for long double del cls._import @staticmethod diff --git a/rpython/rlib/rdynload.py b/rpython/rlib/rdynload.py --- a/rpython/rlib/rdynload.py +++ b/rpython/rlib/rdynload.py @@ -98,8 +98,15 @@ try: ctypes.CDLL(name) except OSError as e: + # common case: ctypes fails too, with the real dlerror() + # message in str(e). Return that error message. return str(e) else: + # uncommon case: may happen if 'name' is a linker script + # (which the C-level dlopen() can't handle) and we are + # directly running on pypy (whose implementation of ctypes + # or cffi will resolve linker scripts). In that case, + # unsure what we can do. return ("opening %r with ctypes.CDLL() works, " "but not with c_dlopen()??" % (name,)) @@ -160,7 +167,18 @@ mode = _dlopen_default_mode() elif (mode & (RTLD_LAZY | RTLD_NOW)) == 0: mode |= RTLD_NOW + # + # haaaack for 'pypy py.test -A' if libm.so is a linker script + # (see reason in _dlerror_on_dlopen_untranslated()) + must_free = False + if not we_are_translated() and platform.name == "linux": + if name and rffi.charp2str(name) == 'libm.so': + name = rffi.str2charp('libm.so.6') + must_free = True + # res = c_dlopen(name, rffi.cast(rffi.INT, mode)) + if must_free: + rffi.free_charp(name) if not res: if not we_are_translated(): err = _dlerror_on_dlopen_untranslated(name) diff --git a/rpython/rlib/rmarshal.py b/rpython/rlib/rmarshal.py --- a/rpython/rlib/rmarshal.py +++ b/rpython/rlib/rmarshal.py @@ -346,11 +346,15 @@ # on s_bigger. It relies on the fact that s_bigger was created with # an expression like 'annotation([s_item])' which returns a ListDef with # no bookkeeper, on which side-effects are not allowed. + saved = annmodel.TLS.allow_int_to_float try: + annmodel.TLS.allow_int_to_float = False s_union = annmodel.unionof(s_bigger, s_smaller) return s_bigger.contains(s_union) except (annmodel.UnionError, TooLateForChange): return False + finally: + annmodel.TLS.allow_int_to_float = saved class __extend__(pairtype(MTag, annmodel.SomeObject)): diff --git a/rpython/rlib/ropenssl.py b/rpython/rlib/ropenssl.py --- a/rpython/rlib/ropenssl.py +++ b/rpython/rlib/ropenssl.py @@ -97,6 +97,21 @@ OPENSSL_VERSION_NUMBER = cconfig["OPENSSL_VERSION_NUMBER"] HAVE_TLSv1_2 = OPENSSL_VERSION_NUMBER >= 0x10001000 +if OPENSSL_VERSION_NUMBER >= 0x10100000: + eci.pre_include_bits = () + eci.post_include_bits = () + raise Exception("""OpenSSL version >= 1.1 not supported yet. + + This program requires OpenSSL version 1.0.x, and may also + work with LibreSSL or OpenSSL 0.9.x. OpenSSL 1.1 is quite + some work to update to; contributions are welcome. Sorry, + you need to install an older version of OpenSSL for now. + Make sure this older version is the one picked up by this + program when it runs the compiler. + + This is the configuration used: %r""" % (eci,)) + + class CConfig: _compilation_info_ = eci diff --git a/rpython/rlib/runicode.py b/rpython/rlib/runicode.py --- a/rpython/rlib/runicode.py +++ b/rpython/rlib/runicode.py @@ -1,5 +1,5 @@ import sys -from rpython.rlib.objectmodel import specialize, we_are_translated +from rpython.rlib.objectmodel import specialize, we_are_translated, enforceargs from rpython.rlib.rstring import StringBuilder, UnicodeBuilder from rpython.rlib.rarithmetic import r_uint, intmask, widen from rpython.rlib.unicodedata import unicodedb @@ -137,7 +137,29 @@ result=result) return result.build(), pos -@specialize.argtype(6) +def _invalid_cont_byte(ordch): + return ordch>>6 != 0x2 # 0b10 + +_invalid_byte_2_of_2 = _invalid_cont_byte +_invalid_byte_3_of_3 = _invalid_cont_byte +_invalid_byte_3_of_4 = _invalid_cont_byte +_invalid_byte_4_of_4 = _invalid_cont_byte + +@enforceargs(allow_surrogates=bool) +def _invalid_byte_2_of_3(ordch1, ordch2, allow_surrogates): + return (ordch2>>6 != 0x2 or # 0b10 + (ordch1 == 0xe0 and ordch2 < 0xa0) + # surrogates shouldn't be valid UTF-8! + or (ordch1 == 0xed and ordch2 > 0x9f and not allow_surrogates)) + +def _invalid_byte_2_of_4(ordch1, ordch2): + return (ordch2>>6 != 0x2 or # 0b10 + (ordch1 == 0xf0 and ordch2 < 0x90) or + (ordch1 == 0xf4 and ordch2 > 0x8f)) + +# note: this specialize() is here for rtyper/rstr.py, which calls this +# function too but with its own fixed errorhandler +@specialize.arg_or_var(4) def str_decode_utf_8_impl(s, size, errors, final, errorhandler, allow_surrogates, result): if size == 0: @@ -157,22 +179,23 @@ if pos + n > size: if not final: break + # argh, this obscure block of code is mostly a copy of + # what follows :-( charsleft = size - pos - 1 # either 0, 1, 2 - # note: when we get the 'unexpected end of data' we don't care - # about the pos anymore and we just ignore the value + # note: when we get the 'unexpected end of data' we need + # to care about the pos returned; it can be lower than size, + # in case we need to continue running this loop if not charsleft: # there's only the start byte and nothing else r, pos = errorhandler(errors, 'utf8', 'unexpected end of data', s, pos, pos+1) result.append(r) - break _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit