Author: Matti Picus <matti.pi...@gmail.com> Branch: release-pypy3.5-5.x Changeset: r92567:d72f9800a42b Date: 2017-10-03 13:53 +0300 http://bitbucket.org/pypy/pypy/changeset/d72f9800a42b/
Log: merge py3.5 into release diff too long, truncating to 2000 out of 2653 lines diff --git a/LICENSE b/LICENSE --- a/LICENSE +++ b/LICENSE @@ -60,8 +60,8 @@ Wim Lavrijsen Eric van Riet Paap Richard Emslie + Remi Meier Alexander Schremmer - Remi Meier Dan Villiom Podlaski Christiansen Lukas Diekmann Sven Hager @@ -102,6 +102,7 @@ Michael Foord Stephan Diehl Stefano Rivera + Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi @@ -110,14 +111,13 @@ Bob Ippolito Bruno Gola David Malcolm - Jean-Paul Calderone Squeaky Edd Barrett Timo Paulssen Marius Gedminas + Nicolas Truessel Alexandre Fayolle Simon Burton - Nicolas Truessel Martin Matusiak Laurence Tratt Wenzhu Man @@ -156,6 +156,7 @@ Stefan H. Muller Tim Felgentreff Eugene Oden + Dodan Mihai Jeff Terrace Henry Mason Vasily Kuznetsov @@ -182,11 +183,13 @@ Rocco Moretti Gintautas Miliauskas Lucian Branescu Mihaila + Mariano Anaya anatoly techtonik - Dodan Mihai Karl Bartel + Stefan Beyer Gabriel Lavoie Jared Grubb + Alecsandru Patrascu Olivier Dormond Wouter van Heyst Sebastian Pawluś @@ -194,6 +197,7 @@ Victor Stinner Andrews Medina Aaron Iles + p_ziesch...@yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -204,6 +208,7 @@ Michael Cheng Mikael Schönenberg Stanislaw Halik + Mihnea Saracin Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -214,14 +219,12 @@ Jonathan David Riehl Beatrice During Alex Perry - p_ziesch...@yahoo.de Robert Zaremba Alan McIntyre Alexander Sedov Vaibhav Sood Reuben Cummings Attila Gobi - Alecsandru Patrascu Christopher Pope Tristan Arthur Christian Tismer @@ -243,7 +246,6 @@ Jacek Generowicz Sylvain Thenault Jakub Stasiak - Stefan Beyer Andrew Dalke Alejandro J. Cura Vladimir Kryachko @@ -275,6 +277,7 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + afteryu Toni Mattis Laurens Van Houtven Bobby Impollonia @@ -305,6 +308,7 @@ Anna Katrina Dominguez Kim Jin Su Amber Brown + Anthony Sottile Nate Bragg Ben Darnell Juan Francisco Cantero Hurtado @@ -325,12 +329,14 @@ Mike Bayer Rodrigo Araújo Daniil Yarancev + Min RK OlivierBlanvillain Jonas Pfannschmidt Zearin Andrey Churin Dan Crosta reub...@gmail.com + Stanisław Halik Julien Phalip Roman Podoliaka Eli Stevens diff --git a/lib-python/2.7/inspect.py b/lib-python/2.7/inspect.py --- a/lib-python/2.7/inspect.py +++ b/lib-python/2.7/inspect.py @@ -203,7 +203,7 @@ f_locals local namespace seen by this frame f_restricted 0 or 1 if frame is in restricted execution mode f_trace tracing function for this frame, or None""" - return isinstance(object, types.FrameType) + return isinstance(object, (types.FrameType, types.FakeFrameType)) def iscode(object): """Return true if the object is a code object. diff --git a/lib-python/2.7/types.py b/lib-python/2.7/types.py --- a/lib-python/2.7/types.py +++ b/lib-python/2.7/types.py @@ -71,6 +71,12 @@ FrameType = type(tb.tb_frame) del tb +# PyPy extension +try: + FakeFrameType = type(next(sys._current_frames().itervalues())) +except (AttributeError, StopIteration): + FakeFrameType = FrameType + SliceType = slice EllipsisType = type(Ellipsis) 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.11.0 +Version: 1.11.1 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 from .error import CDefError, FFIError, VerificationError, VerificationMissing -__version__ = "1.11.0" -__version_info__ = (1, 11, 0) +__version__ = "1.11.1" +__version_info__ = (1, 11, 1) # 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/_embedding.h b/lib_pypy/cffi/_embedding.h --- a/lib_pypy/cffi/_embedding.h +++ b/lib_pypy/cffi/_embedding.h @@ -247,7 +247,7 @@ if (f != NULL && f != Py_None) { PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.11.0" + "\ncompiled with cffi version: 1.11.1" "\n_cffi_backend module: ", f); modules = PyImport_GetModuleDict(); mod = PyDict_GetItemString(modules, "_cffi_backend"); diff --git a/lib_pypy/pyrepl/historical_reader.py b/lib_pypy/pyrepl/historical_reader.py --- a/lib_pypy/pyrepl/historical_reader.py +++ b/lib_pypy/pyrepl/historical_reader.py @@ -17,7 +17,7 @@ # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -from pyrepl import reader, commands +from pyrepl import reader, commands, input from pyrepl.reader import Reader as R isearch_keymap = tuple( @@ -215,7 +215,6 @@ isearch_forwards, isearch_backwards, operate_and_get_next]: self.commands[c.__name__] = c self.commands[c.__name__.replace('_', '-')] = c - from pyrepl import input self.isearch_trans = input.KeymapTranslator( isearch_keymap, invalid_cls=isearch_end, character_cls=isearch_add_character) diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -27,8 +27,8 @@ Wim Lavrijsen Eric van Riet Paap Richard Emslie + Remi Meier Alexander Schremmer - Remi Meier Dan Villiom Podlaski Christiansen Lukas Diekmann Sven Hager @@ -69,6 +69,7 @@ Michael Foord Stephan Diehl Stefano Rivera + Jean-Paul Calderone Stefan Schwarzer Tomek Meka Valentino Volonghi @@ -77,14 +78,13 @@ Bob Ippolito Bruno Gola David Malcolm - Jean-Paul Calderone Squeaky Edd Barrett Timo Paulssen Marius Gedminas + Nicolas Truessel Alexandre Fayolle Simon Burton - Nicolas Truessel Martin Matusiak Laurence Tratt Wenzhu Man @@ -123,6 +123,7 @@ Stefan H. Muller Tim Felgentreff Eugene Oden + Dodan Mihai Jeff Terrace Henry Mason Vasily Kuznetsov @@ -149,11 +150,13 @@ Rocco Moretti Gintautas Miliauskas Lucian Branescu Mihaila + Mariano Anaya anatoly techtonik - Dodan Mihai Karl Bartel + Stefan Beyer Gabriel Lavoie Jared Grubb + Alecsandru Patrascu Olivier Dormond Wouter van Heyst Sebastian Pawluś @@ -161,6 +164,7 @@ Victor Stinner Andrews Medina Aaron Iles + p_ziesch...@yahoo.de Toby Watson Daniel Patrick Stuart Williams @@ -171,6 +175,7 @@ Michael Cheng Mikael Schönenberg Stanislaw Halik + Mihnea Saracin Berkin Ilbeyi Gasper Zejn Faye Zhao @@ -181,14 +186,12 @@ Jonathan David Riehl Beatrice During Alex Perry - p_ziesch...@yahoo.de Robert Zaremba Alan McIntyre Alexander Sedov Vaibhav Sood Reuben Cummings Attila Gobi - Alecsandru Patrascu Christopher Pope Tristan Arthur Christian Tismer @@ -210,7 +213,6 @@ Jacek Generowicz Sylvain Thenault Jakub Stasiak - Stefan Beyer Andrew Dalke Alejandro J. Cura Vladimir Kryachko @@ -242,6 +244,7 @@ Christoph Gerum Miguel de Val Borro Artur Lisiecki + afteryu Toni Mattis Laurens Van Houtven Bobby Impollonia @@ -272,6 +275,7 @@ Anna Katrina Dominguez Kim Jin Su Amber Brown + Anthony Sottile Nate Bragg Ben Darnell Juan Francisco Cantero Hurtado @@ -292,12 +296,14 @@ Mike Bayer Rodrigo Araújo Daniil Yarancev + Min RK OlivierBlanvillain Jonas Pfannschmidt Zearin Andrey Churin Dan Crosta reub...@gmail.com + Stanisław Halik Julien Phalip Roman Podoliaka Eli Stevens diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst --- a/pypy/doc/cpython_differences.rst +++ b/pypy/doc/cpython_differences.rst @@ -436,7 +436,8 @@ * the ``__builtins__`` name is always referencing the ``__builtin__`` module, never a dictionary as it sometimes is in CPython. Assigning to - ``__builtins__`` has no effect. + ``__builtins__`` has no effect. (For usages of tools like + RestrictedPython, see `issue #2653`_.) * directly calling the internal magic methods of a few built-in types with invalid arguments may have a slightly different result. For @@ -556,4 +557,4 @@ .. _`is ignored in PyPy`: http://bugs.python.org/issue14621 .. _`little point`: http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html .. _`#2072`: https://bitbucket.org/pypy/pypy/issue/2072/ - +.. _`issue #2653`: https://bitbucket.org/pypy/pypy/issues/2653/ 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-v5.9.0.rst release-v5.8.0.rst release-v5.7.1.rst release-v5.7.0.rst diff --git a/pypy/doc/index-of-whatsnew.rst b/pypy/doc/index-of-whatsnew.rst --- a/pypy/doc/index-of-whatsnew.rst +++ b/pypy/doc/index-of-whatsnew.rst @@ -7,6 +7,7 @@ .. toctree:: whatsnew-head.rst + whatsnew-pypy2-5.9.0.rst whatsnew-pypy2-5.8.0.rst whatsnew-pypy2-5.7.0.rst whatsnew-pypy2-5.6.0.rst @@ -36,6 +37,7 @@ .. toctree:: whatsnew-pypy3-head.rst + whatsnew-pypy3-5.9.0.rst whatsnew-pypy3-5.8.0.rst whatsnew-pypy3-5.7.0.rst diff --git a/pypy/doc/release-v5.9.0.rst b/pypy/doc/release-v5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/release-v5.9.0.rst @@ -0,0 +1,217 @@ +===================================== +PyPy2.7 and PyPy3.5 v5.9 dual release +===================================== + +The PyPy team is proud to release both PyPy2.7 v5.9 (an interpreter supporting +Python 2.7 syntax), and a beta-quality PyPy3.5 v5.9 (an interpreter for Python +3.5 syntax). The two releases are both based on much the same codebase, thus +the dual release. Note that PyPy3.5 supports Linux 64bit only for now. + +This new PyPy2.7 release includes the upstream stdlib version 2.7.13, and +PyPy3.5 includes the upstream stdlib version 3.5.3. + +NumPy and Pandas now work on PyPy2.7 (together with Cython 0.27.1). Issues +that appeared as excessive memory +use were cleared up and other incompatibilities were resolved. The C-API +compatibility layer does slow down code which crosses the python-c interface +often, we have ideas on how it could be improved, and still recommend +using pure python on PyPy or interfacing via CFFI_. Many other modules +based on C-API exentions now work on PyPy as well. + +Cython 0.27.1 (released very recently) supports more projects with PyPy, both +on PyPy2.7 and PyPy3.5 beta. Note version **0.27.1** is now the minimum +version that supports this version of PyPy, due to some interactions with +updated C-API interface code. + +We optimized the JSON parser for recurring string keys, which should decrease +memory use to 50% and increase parsing speed by up to 15% for large JSON files +with many repeating dictionary keys (which is quite common). + +CFFI_, which is part of the PyPy release, has been updated to 1.11.1, +improving an already great package for interfacing with C. CFFI now supports +complex arguments in API mode, as well as ``char16_t`` and ``char32_t`` and has +improved support for callbacks. + +Please let us know if your use case is slow, we have ideas how to make things +faster but need real-world examples (not micro-benchmarks) of problematic code. + +Work sponsored by a Mozilla grant_ continues on PyPy3.5; numerous fixes from +CPython were ported to PyPy. Of course the bug fixes and performance enhancements +mentioned above are part of both PyPy2.7 and PyPy3.5 beta. + +As always, this release fixed many other issues and bugs raised by the +growing community of PyPy users. We strongly recommend updating. + +You can download the v5.9 releases here: + + http://pypy.org/download.html + +We would like to thank our donors for the continued support of the PyPy +project. + +We would also like to thank our contributors and +encourage new people to join the project. PyPy has many +layers and we need help with all of them: `PyPy`_ and `RPython`_ documentation +improvements, tweaking popular `modules`_ to run on pypy, or general `help`_ +with making RPython's JIT even better. + +.. _vmprof: http://vmprof.readthedocs.io +.. _CFFI: https://cffi.readthedocs.io/en/latest/whatsnew.html +.. _grant: https://morepypy.blogspot.com/2016/08/pypy-gets-funding-from-mozilla-for.html +.. _`PyPy`: index.html +.. _`RPython`: https://rpython.readthedocs.org +.. _`modules`: project-ideas.html#make-more-python-modules-pypy-friendly +.. _`help`: project-ideas.html + +What is PyPy? +============= + +PyPy is a very compliant Python interpreter, almost a drop-in replacement for +CPython 2.7 and CPython 3.5. 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. + +The PyPy 2.7 release 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 and CPython 2.7.x`: http://speed.pypy.org +.. _`dynamic languages`: http://rpython.readthedocs.io/en/latest/examples.html + +Highlights of the PyPy2.7, cpyext, and RPython changes (since 5.8 released June, 2017) +====================================================================================== + +See also issues that were resolved_ + +Note that these are also merged into PyPy 3.5 + +* New features and cleanups + + * Add support for ``PyFrozenSet_New``, ``PyObject_HashNotImplemented``, + ``PyObject_Print(NULL, ...)``, ``PyObject_RichCompareBool(a, a, ...)``, + ``PyType_IS_GC`` (does nothing), ``PyUnicode_FromFormat`` + * ctypes ``char_p`` and ``unichar_p`` indexing now CPython compatible + * ``gcdump`` now reports largest object + * More complete support in the ``_curses`` CFFI module + * Add cPickle.Unpickler.find_global (issue 1853_) + * Fix ``PyErr_Fetch`` + ``PyErr_NormalizeException`` with no exception set + * Simplify ``gc.get_referrers()`` to return the opposite of ``gc.get_referents()`` + * Update RevDB to version pypy2.7-v5.6.2 + * Previously, ``instance.method`` would return always the same bound method + object, when gotten from the same instance (as far as ``is`` and ``id()`` + can tell). CPython doesn't do that. Now PyPy, like CPython, returns a + different bound method object every time. For ``type.method``, PyPy2 still + returns always the same *unbound* method object; CPython does it for built-in + types but not for user-defined types + * Link to disable PaX protection for the JIT when needed + * Update build instructions and an rarely used Makefile + * Recreate support for using leakfinder in cpyext tests which had suffered + bit-rot, disable due to many false positives + * Add more functionality to ``sysconfig`` + * Added ``_swappedbytes_`` support for ``ctypes.Structure`` + * Better support the ``inspect`` module on ``frames`` + +* Bug Fixes + + * Fix issue 2592_ - cpyext ``PyListObject.pop``, ``pop_end`` must return a value + * Implement ``PyListOjbect.getstorage_copy`` + * Fix for ``reversed(dictproxy)`` issue 2601_ + * Fix for duplicate names in ctypes' ``_fields__``, issue 2621_ + * Update built-in ``pyexpat`` module on win32 to use UTF-8 version not UTF-16 + * ``gc.get_objects`` now handles objects with finalizers more consistently + * Fixed memory leak in ``SSLContext.getpeercert`` returning validated + certificates and ``SSLContext.get_ca_certs(binary_mode=True)`` + (_get_crl_dp) `CPython issue 29738`_ + +* Performance improvements: + + * Improve performance of ``bytearray.extend`` by rewriting portions in app-level + * Optimize list accesses with constant indexes better by retaining more + information about them + * Add a jit driver for ``array.count`` and ``array.index`` + * Improve information retained in a bridge wrt ``array`` + * Move some dummy CAPI functions and ``Py*_Check`` functions from RPython into + pure C macros + * In the fast ``zip(intlist1, intlist2)`` implementation, don't wrap and unwrap + all the ints + * Cache string keys that occur in JSON dicts, as they are likely to repeat + +* RPython improvements + + * Do not preallocate a RPython list if we only know an upper bound on its size + * Issue 2590_: fix the bounds in the GC when allocating a lot of objects with finalizers + * Replace magical NOT RPYTHON comment with a decorator + * Implement ``socket.sendmsg()``/``.recvmsg()`` for py3.5 + * Add ``memory_pressure`` for ``_SSLSocket`` objects + +* Degredations + + * Disable vmprof on win32, due to upstream changes that break the internal ``_vmprof`` module + +.. _here: cpython_differences.html +.. _1853: https://bitbucket.org/pypy/pypy/issues/1853 +.. _2592: https://bitbucket.org/pypy/pypy/issues/2592 +.. _2590: https://bitbucket.org/pypy/pypy/issues/2590 +.. _2621: https://bitbucket.org/pypy/pypy/issues/2621 + +Highlights of the PyPy3.5 release (since 5.8 beta released June 2017) +====================================================================== + +* New features + + * Add support for ``_PyNamespace_New``, ``PyMemoryView_FromMemory``, + ``Py_EnterRecursiveCall`` raising RecursionError, ``PyObject_LengthHint``, + ``PyUnicode_FromKindAndData``, ``PyDict_SetDefault``, ``PyGenObject``, + ``PyGenObject``, ``PyUnicode_Substring``, ``PyLong_FromUnicodeObject`` + * Implement ``PyType_FromSpec`` (PEP 384) and fix issues with PEP 489 support + * Support the new version of ``os.stat()`` on win32 + * Use ``stat3()`` on Posix + * Accept buffer objects as filenames, except for `oslistdir`` + * Make slices of array ``memoryview`` s usable as writable buffers if contiguous + * Better handling of ``'%s'`` formatting for byte strings which might be utf-8 encoded + * Update the macros ``Py_DECREF`` and similar to use the CPython 3.5 version + * Ensure that ``mappingproxy`` is recognised as a mapping, not a sequence + * Enable PGO for CLang + * Rework ``cppyy`` packaging and rename the backend to ``_cppyy`` + * Support for libressl 2.5.4 + * Mirror CPython ``classmethod __reduce__`` which fixes pickling test + * Use utf-8 for ``readline`` history file + * Allow assigning ``'__class__'`` between ``ModuleType`` and its subclasses + * Add async slot functions in cpyext + +* Bug Fixes + + * Try to make ``openssl`` CFFI bindings more general and future-proof + * Better support ``importlib`` by only listing built-in modules in ``sys.builtin`` + * Add ``memory_pressure`` to large CFFI allocations in ``_lzma``, issue 2579_ + * Fix for ``reversed(mapping object)`` issue 2601_ + * Fixing regression with non-started generator receiving non-``None``, should + always raise ``TypeError`` + * ``itertools.islice``: use same logic as CPython, fixes 2643_ + +* Performance improvements: + + * + +* The following features of Python 3.5 are not implemented yet in PyPy: + + * PEP 442: Safe object finalization + +.. _resolved: whatsnew-pypy2-5.9.0.html +.. _2579: https://bitbucket.org/pypy/pypy/issues/2579 +.. _2601: https://bitbucket.org/pypy/pypy/issues/2601 +.. _2643: https://bitbucket.org/pypy/pypy/issues/2643 +.. _CPython issue 29738: https://bugs.python.org/issue29738 + +Please update, and continue to help us make PyPy better. + +Cheers 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 @@ -1,87 +1,15 @@ -========================== -What's new in PyPy2.7 5.9+ -========================== - -.. this is a revision shortly after release-pypy2.7-v5.8.0 -.. startrev: 558bd00b3dd8 - -In previous versions of PyPy, ``instance.method`` would return always -the same bound method object, when gotten out of the same instance (as -far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now -PyPy, like CPython, returns a different bound method object every time. -For ``type.method``, PyPy2 still returns always the same *unbound* -method object; CPython does it for built-in types but not for -user-defined types. - -.. branch: cffi-complex -.. branch: cffi-char16-char32 - -The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. - -.. branch: ctypes_char_indexing - -Indexing into char* behaves differently than CPython - -.. branch: vmprof-0.4.8 - -Improve and fix issues with vmprof - -.. branch: issue-2592 - -CPyext PyListObject.pop must return the value - -.. branch: cpyext-hash_notimpl - -If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None - -.. branch: cppyy-packaging - -Renaming of ``cppyy`` to ``_cppyy``. -The former is now an external package installable with ``pip install cppyy``. - -.. branch: Enable_PGO_for_clang - -.. branch: nopax - -At the end of translation, run ``attr -q -s pax.flags -V m`` on -PAX-enabled systems on the produced binary. This seems necessary -because PyPy uses a JIT. - -.. branch: pypy_bytearray - -Improve ``bytearray`` performance (backported from py3.5) - -.. branch: gc-del-limit-growth - -Fix the bounds in the GC when allocating a lot of objects with finalizers, -fixes issue #2590 - -.. branch: arrays-force-less - -Small improvement to optimize list accesses with constant indexes better by -throwing away information about them less eagerly. - - -.. branch: getarrayitem-into-bridges - -More information is retained into a bridge: knowledge about the content of -arrays (at fixed indices) is stored in guards (and thus available at the -beginning of bridges). Also, some better feeding of information about known -fields of constant objects into bridges. - -.. branch: cpyext-leakchecking - -Add support for leakfinder in cpyext tests (disabled for now, due to too many -failures). - -.. branch: pypy_swappedbytes - -Added ``_swappedbytes_`` support for ``ctypes.Structure`` - -.. branch: pycheck-macros - -Convert many Py*_Check cpyext functions into macros, like CPython. - -.. branch: py_ssize_t - -Explicitly use Py_ssize_t as the Signed type in pypy c-api +=========================== +What's new in PyPy2.7 5.10+ +=========================== + +.. this is a revision shortly after release-pypy2.7-v5.9.0 +.. startrev:899e5245de1e + +.. branch: cpyext-jit + +Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext: +this allows to write specialized code which is much faster than previous +completely generic version. Moreover, let the JIT to look inside the cpyext +module: the net result is that cpyext calls are up to 7x faster. However, this +is true only for very simple situations: in all real life code, we are still +much slower than CPython (more optimizations to come) diff --git a/pypy/doc/whatsnew-pypy2-5.9.0.rst b/pypy/doc/whatsnew-pypy2-5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy2-5.9.0.rst @@ -0,0 +1,87 @@ +========================= +What's new in PyPy2.7 5.9 +========================= + +.. this is a revision shortly after release-pypy2.7-v5.8.0 +.. startrev: 558bd00b3dd8 + +In previous versions of PyPy, ``instance.method`` would return always +the same bound method object, when gotten out of the same instance (as +far as ``is`` and ``id()`` can tell). CPython doesn't do that. Now +PyPy, like CPython, returns a different bound method object every time. +For ``type.method``, PyPy2 still returns always the same *unbound* +method object; CPython does it for built-in types but not for +user-defined types. + +.. branch: cffi-complex +.. branch: cffi-char16-char32 + +The two ``cffi-*`` branches are part of the upgrade to cffi 1.11. + +.. branch: ctypes_char_indexing + +Indexing into char* behaves differently than CPython + +.. branch: vmprof-0.4.8 + +Improve and fix issues with vmprof + +.. branch: issue-2592 + +CPyext PyListObject.pop must return the value + +.. branch: cpyext-hash_notimpl + +If ``tp_hash`` is ``PyObject_HashNotImplemented``, set ``obj.__dict__['__hash__']`` to None + +.. branch: cppyy-packaging + +Renaming of ``cppyy`` to ``_cppyy``. +The former is now an external package installable with ``pip install cppyy``. + +.. branch: Enable_PGO_for_clang + +.. branch: nopax + +At the end of translation, run ``attr -q -s pax.flags -V m`` on +PAX-enabled systems on the produced binary. This seems necessary +because PyPy uses a JIT. + +.. branch: pypy_bytearray + +Improve ``bytearray`` performance (backported from py3.5) + +.. branch: gc-del-limit-growth + +Fix the bounds in the GC when allocating a lot of objects with finalizers, +fixes issue #2590 + +.. branch: arrays-force-less + +Small improvement to optimize list accesses with constant indexes better by +throwing away information about them less eagerly. + + +.. branch: getarrayitem-into-bridges + +More information is retained into a bridge: knowledge about the content of +arrays (at fixed indices) is stored in guards (and thus available at the +beginning of bridges). Also, some better feeding of information about known +fields of constant objects into bridges. + +.. branch: cpyext-leakchecking + +Add support for leakfinder in cpyext tests (disabled for now, due to too many +failures). + +.. branch: pypy_swappedbytes + +Added ``_swappedbytes_`` support for ``ctypes.Structure`` + +.. branch: pycheck-macros + +Convert many Py*_Check cpyext functions into macros, like CPython. + +.. branch: py_ssize_t + +Explicitly use Py_ssize_t as the Signed type in pypy c-api diff --git a/pypy/doc/whatsnew-pypy3-5.9.0.rst b/pypy/doc/whatsnew-pypy3-5.9.0.rst new file mode 100644 --- /dev/null +++ b/pypy/doc/whatsnew-pypy3-5.9.0.rst @@ -0,0 +1,7 @@ +======================= +What's new in PyPy3 5.9 +======================= + +.. this is the revision after release-pypy3.5-5.8 +.. startrev: afbf09453369 + diff --git a/pypy/doc/whatsnew-pypy3-head.rst b/pypy/doc/whatsnew-pypy3-head.rst --- a/pypy/doc/whatsnew-pypy3-head.rst +++ b/pypy/doc/whatsnew-pypy3-head.rst @@ -1,9 +1,9 @@ ========================= -What's new in PyPy3 5.8+ +What's new in PyPy3 5.9+ ========================= -.. this is the revision after release-pypy3.3-5.8.x was branched -.. startrev: c173df164527 +.. this is the revision after release-pypy3.5-5.9 +.. startrev: be41e3ac0a29 .. branch: multiphase diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -1162,9 +1162,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-c.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.setup_bootstrap_path(pypy_c) newpath = sys.path[:] # we get at least lib_pypy @@ -1180,9 +1184,13 @@ import os old_sys_path = sys.path[:] sys.path.append(self.goal_dir) + if sys.platform == 'win32': + exename = 'pypy3-c.exe' + else: + exename = 'pypy3-c' try: import app_main - pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', 'pypy3-c') + pypy_c = os.path.join(self.trunkdir, 'pypy', 'goal', exename) app_main.entry_point(pypy_c, [self.foo_py]) # assert it did not crash finally: diff --git a/pypy/module/__builtin__/test/test_descriptor.py b/pypy/module/__builtin__/test/test_descriptor.py --- a/pypy/module/__builtin__/test/test_descriptor.py +++ b/pypy/module/__builtin__/test/test_descriptor.py @@ -258,6 +258,17 @@ raises(TypeError, "super(D).__get__(12)") raises(TypeError, "super(D).__get__(C())") + def test_super_incomplete(self): + """ + class M(type): + def mro(cls): + if cls.__mro__ is None: + raises(AttributeError, lambda: super(cls, cls).xxx) + return type.mro(cls) + class A(metaclass=M): + pass + """ + def test_classmethods_various(self): class C(object): def foo(*a): return a 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 from rpython.rtyper.lltypesystem import rffi -VERSION = "1.11.0" +VERSION = "1.11.1" FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI try: diff --git a/pypy/module/_cffi_backend/cffi1_module.py b/pypy/module/_cffi_backend/cffi1_module.py --- a/pypy/module/_cffi_backend/cffi1_module.py +++ b/pypy/module/_cffi_backend/cffi1_module.py @@ -1,4 +1,5 @@ from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.error import oefmt from pypy.interpreter.module import Module @@ -15,7 +16,7 @@ INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void)) - +@jit.dont_look_inside def load_cffi1_module(space, name, path, initptr): # This is called from pypy.module.cpyext.api.load_extension_module() from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py --- a/pypy/module/_cffi_backend/ctypeptr.py +++ b/pypy/module/_cffi_backend/ctypeptr.py @@ -158,10 +158,11 @@ class W_CTypePtrBase(W_CTypePtrOrArray): # base class for both pointers and pointers-to-functions - _attrs_ = ['is_void_ptr', 'is_voidchar_ptr'] - _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr'] + _attrs_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] + _immutable_fields_ = ['is_void_ptr', 'is_voidchar_ptr', 'is_onebyte_ptr'] is_void_ptr = False is_voidchar_ptr = False + is_onebyte_ptr = False def convert_to_object(self, cdata): ptrdata = rffi.cast(rffi.CCHARPP, cdata)[0] @@ -181,12 +182,20 @@ if self.is_void_ptr or other.is_void_ptr: pass # cast from or to 'void *' elif self.is_voidchar_ptr or other.is_voidchar_ptr: - space = self.space - msg = ("implicit cast from '%s' to '%s' " - "will be forbidden in the future (check that the types " - "are as you expect; use an explicit ffi.cast() if they " - "are correct)" % (other.name, self.name)) - space.warn(space.newtext(msg), space.w_UserWarning) + # for backward compatibility, accept "char *" as either + # source of target. This is not what C does, though, + # so emit a warning that will eventually turn into an + # error. The warning is turned off if both types are + # pointers to single bytes. + if self.is_onebyte_ptr and other.is_onebyte_ptr: + pass # no warning + else: + space = self.space + msg = ("implicit cast from '%s' to '%s' " + "will be forbidden in the future (check that the types " + "are as you expect; use an explicit ffi.cast() if they " + "are correct)" % (other.name, self.name)) + space.warn(space.newtext(msg), space.w_UserWarning) else: raise self._convert_error("compatible pointer", w_ob) @@ -216,6 +225,7 @@ self.is_void_ptr = isinstance(ctitem, ctypevoid.W_CTypeVoid) self.is_voidchar_ptr = (self.is_void_ptr or isinstance(ctitem, ctypeprim.W_CTypePrimitiveChar)) + self.is_onebyte_ptr = (ctitem.size == 1) W_CTypePtrBase.__init__(self, space, size, extra, 2, ctitem) def newp(self, w_init, allocator): 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.11.0", ("This test_c.py file is for testing a version" +assert __version__ == "1.11.1", ("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,): @@ -2099,7 +2099,8 @@ if sys.platform.startswith("linux"): BWChar = new_primitive_type("wchar_t") assert sizeof(BWChar) == 4 - assert int(cast(BWChar, -1)) == -1 # signed, on linux + # wchar_t is often signed on Linux, but not always (e.g. on ARM) + assert int(cast(BWChar, -1)) in (-1, 4294967295) def test_char16(): BChar16 = new_primitive_type("char16_t") @@ -3903,9 +3904,11 @@ BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) + BUCharP = new_pointer_type(new_primitive_type("unsigned char")) z1 = cast(BCharP, 0) z2 = cast(BIntP, 0) z3 = cast(BVoidP, 0) + z4 = cast(BUCharP, 0) with warnings.catch_warnings(record=True) as w: newp(new_pointer_type(BIntP), z1) # warn assert len(w) == 1 @@ -3919,6 +3922,12 @@ assert len(w) == 2 newp(new_pointer_type(BIntP), z3) # fine assert len(w) == 2 + newp(new_pointer_type(BCharP), z4) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z1) # fine (ignore signedness here) + assert len(w) == 2 + newp(new_pointer_type(BUCharP), z3) # fine + assert len(w) == 2 # check that the warnings are associated with lines in this file assert w[1].lineno == w[0].lineno + 4 diff --git a/pypy/module/_pypyjson/interp_decoder.py b/pypy/module/_pypyjson/interp_decoder.py --- a/pypy/module/_pypyjson/interp_decoder.py +++ b/pypy/module/_pypyjson/interp_decoder.py @@ -1,6 +1,6 @@ import sys from rpython.rlib.rstring import StringBuilder -from rpython.rlib.objectmodel import specialize, always_inline +from rpython.rlib.objectmodel import specialize, always_inline, r_dict from rpython.rlib import rfloat, runicode from rpython.rtyper.lltypesystem import lltype, rffi from pypy.interpreter.error import oefmt, OperationError @@ -42,6 +42,22 @@ ll_res.chars[i] = cast_primitive(UniChar, ch) return hlunicode(ll_res) +def slice_eq(a, b): + (ll_chars1, start1, length1, _) = a + (ll_chars2, start2, length2, _) = b + if length1 != length2: + return False + j = start2 + for i in range(start1, start1 + length1): + if ll_chars1[i] != ll_chars2[j]: + return False + j += 1 + return True + +def slice_hash(a): + (ll_chars, start, length, h) = a + return h + class DecoderError(Exception): def __init__(self, msg, pos): self.msg = msg @@ -60,8 +76,7 @@ self.ll_chars = rffi.str2charp(s) self.end_ptr = lltype.malloc(rffi.CCHARPP.TO, 1, flavor='raw') self.pos = 0 - self.last_type = TYPE_UNKNOWN - self.memo = {} + self.cache = r_dict(slice_eq, slice_hash) def close(self): rffi.free_charp(self.ll_chars) @@ -249,21 +264,16 @@ def decode_object(self, i): start = i - w_dict = self.space.newdict() - # + i = self.skip_whitespace(i) if self.ll_chars[i] == '}': self.pos = i+1 - return w_dict - # + return self.space.newdict() + + d = {} while True: # parse a key: value - self.last_type = TYPE_UNKNOWN - w_name = self.decode_any(i) - if self.last_type != TYPE_STRING: - raise DecoderError("Key name must be string for object starting at", start) - w_name = self.memo.setdefault(self.space.unicode_w(w_name), w_name) - + name = self.decode_key(i) i = self.skip_whitespace(self.pos) ch = self.ll_chars[i] if ch != ':': @@ -272,13 +282,13 @@ i = self.skip_whitespace(i) # w_value = self.decode_any(i) - self.space.setitem(w_dict, w_name, w_value) + d[name] = w_value i = self.skip_whitespace(self.pos) ch = self.ll_chars[i] i += 1 if ch == '}': self.pos = i - return w_dict + return self._create_dict(d) elif ch == ',': pass elif ch == '\0': @@ -287,6 +297,9 @@ raise DecoderError("Unexpected '%s' when decoding object" % ch, i-1) + def _create_dict(self, d): + from pypy.objspace.std.dictmultiobject import from_unicode_key_dict + return from_unicode_key_dict(self.space, d) def decode_string(self, i): start = i @@ -298,22 +311,23 @@ i += 1 bits |= ord(ch) if ch == '"': - if bits & 0x80: - # the 8th bit is set, it's an utf8 strnig - content_utf8 = self.getslice(start, i-1) - content_unicode = unicodehelper.decode_utf8(self.space, content_utf8) - else: - # ascii only, fast path (ascii is a strict subset of - # latin1, and we already checked that all the chars are < - # 128) - content_unicode = strslice2unicode_latin1(self.s, start, i-1) - self.last_type = TYPE_STRING self.pos = i - return self.space.newunicode(content_unicode) + return self.space.newunicode( + self._create_string(start, i - 1, bits)) elif ch == '\\' or ch < '\x20': self.pos = i-1 return self.decode_string_escaped(start) + def _create_string(self, start, end, bits): + if bits & 0x80: + # the 8th bit is set, it's an utf8 string + content_utf8 = self.getslice(start, end) + return unicodehelper.decode_utf8(self.space, content_utf8) + else: + # ascii only, fast path (ascii is a strict subset of + # latin1, and we already checked that all the chars are < + # 128) + return strslice2unicode_latin1(self.s, start, end) def decode_string_escaped(self, start): i = self.pos @@ -326,9 +340,7 @@ i += 1 if ch == '"': content_utf8 = builder.build() - content_unicode = unicodehelper.decode_utf8( - self.space, content_utf8, allow_surrogates=True) - self.last_type = TYPE_STRING + content_unicode = unicodehelper.decode_utf8(self.space, content_utf8, allow_surrogates=True) self.pos = i return self.space.newunicode(content_unicode) elif ch == '\\': @@ -390,6 +402,47 @@ lowsurr = int(hexdigits, 16) # the possible ValueError is caugth by the caller return 0x10000 + (((highsurr - 0xd800) << 10) | (lowsurr - 0xdc00)) + def decode_key(self, i): + """ returns an unwrapped unicode """ + from rpython.rlib.rarithmetic import intmask + + i = self.skip_whitespace(i) + ll_chars = self.ll_chars + ch = ll_chars[i] + if ch != '"': + raise DecoderError("Key name must be string at char", i) + i += 1 + + start = i + bits = 0 + strhash = ord(ll_chars[i]) << 7 + while True: + ch = ll_chars[i] + i += 1 + if ch == '"': + break + elif ch == '\\' or ch < '\x20': + self.pos = i-1 + return self.space.unicode_w(self.decode_string_escaped(start)) + strhash = intmask((1000003 * strhash) ^ ord(ll_chars[i])) + bits |= ord(ch) + length = i - start - 1 + if length == 0: + strhash = -1 + else: + strhash ^= length + strhash = intmask(strhash) + self.pos = i + # check cache first: + key = (ll_chars, start, length, strhash) + try: + return self.cache[key] + except KeyError: + pass + res = self._create_string(start, i - 1, bits) + self.cache[key] = res + return res + def loads(space, w_s, w_errorcls=None): s = space.text_w(w_s) decoder = JSONDecoder(space, s) diff --git a/pypy/module/_pypyjson/targetjson.py b/pypy/module/_pypyjson/targetjson.py --- a/pypy/module/_pypyjson/targetjson.py +++ b/pypy/module/_pypyjson/targetjson.py @@ -5,9 +5,15 @@ import time from pypy.interpreter.error import OperationError -from pypy.module._pypyjson.interp_decoder import loads +from pypy.module._pypyjson.interp_decoder import loads, JSONDecoder from rpython.rlib.objectmodel import specialize, dont_inline +def _create_dict(self, d): + w_res = W_Dict() + w_res.dictval = d + return w_res + +JSONDecoder._create_dict = _create_dict ## MSG = open('msg.json').read() @@ -65,10 +71,14 @@ def isinstance_w(self, w_x, w_type): return isinstance(w_x, w_type) - def str_w(self, w_x): + def bytes_w(self, w_x): assert isinstance(w_x, W_String) return w_x.strval + def unicode_w(self, w_x): + assert isinstance(w_x, W_Unicode) + return w_x.unival + @dont_inline def call_method(self, obj, name, arg): assert name == 'append' @@ -83,13 +93,17 @@ assert isinstance(key, W_Unicode) d.dictval[key.unival] = value - def wrapunicode(self, x): + def newunicode(self, x): return W_Unicode(x) - def wrapint(self, x): + def newtext(self, x): + return W_String(x) + newbytes = newtext + + def newint(self, x): return W_Int(x) - def wrapfloat(self, x): + def newfloat(self, x): return W_Float(x) @specialize.argtype(1) diff --git a/pypy/module/_pypyjson/test/test__pypyjson.py b/pypy/module/_pypyjson/test/test__pypyjson.py --- a/pypy/module/_pypyjson/test/test__pypyjson.py +++ b/pypy/module/_pypyjson/test/test__pypyjson.py @@ -10,7 +10,18 @@ assert dec.skip_whitespace(8) == len(s) dec.close() - +def test_decode_key(): + s1 = "123" * 100 + s = ' "%s" "%s" ' % (s1, s1) + dec = JSONDecoder('fake space', s) + assert dec.pos == 0 + x = dec.decode_key(0) + assert x == s1 + # check caching + y = dec.decode_key(dec.pos) + assert y == s1 + assert y is x + dec.close() class AppTest(object): spaceconfig = {"usemodules": ['_pypyjson']} @@ -189,6 +200,12 @@ res = _pypyjson.loads(json) assert res == {u'a': u'\ud83d'} + def test_cache_keys(self): + import _pypyjson + json = '[{"a": 1}, {"a": 2}]' + res = _pypyjson.loads(json) + assert res == [{u'a': 1}, {u'a': 2}] + def test_tab_in_string_should_fail(self): import _pypyjson # http://json.org/JSON_checker/test/fail25.json @@ -226,7 +243,7 @@ ('{"spam":[42}', "Unexpected '}' when decoding array", 11), ('["]', 'Unterminated string starting at', 1), ('["spam":', "Unexpected ':' when decoding array", 7), - ('[{]', "Unexpected ']' at", 2), + ('[{]', "Key name must be string at char", 2), ] for inputtext, errmsg, errpos in test_cases: exc = raises(ValueError, _pypyjson.loads, inputtext) diff --git a/pypy/module/_vmprof/test/test__vmprof.py b/pypy/module/_vmprof/test/test__vmprof.py --- a/pypy/module/_vmprof/test/test__vmprof.py +++ b/pypy/module/_vmprof/test/test__vmprof.py @@ -45,10 +45,10 @@ _, size = struct.unpack("ll", s[i:i + 2 * WORD]) count += 1 i += 2 * WORD + size - elif s[i] == '\x06': + elif s[i] == 6: print(s[i:i+24]) i += 1+8+8+8 - elif s[i] == '\x07': + elif s[i] == 7: i += 1 # skip string size, = struct.unpack("l", s[i:i + WORD]) 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 @@ -14,6 +14,7 @@ from rpython.rlib.rfile import (FILEP, c_fread, c_fclose, c_fwrite, c_fdopen, c_fileno, c_fopen)# for tests +from rpython.rlib import jit from rpython.translator import cdir from rpython.translator.tool.cbuild import ExternalCompilationInfo from rpython.translator.gensupp import NameManager @@ -453,6 +454,11 @@ if func.__name__ in FUNCTIONS_BY_HEADER[header]: raise ValueError("%s already registered" % func.__name__) func._always_inline_ = 'try' + # + # XXX: should we @jit.dont_look_inside all the @cpython_api functions, + # or we should only disable some of them? + func._jit_look_inside_ = False + # api_function = ApiFunction( argtypes, restype, func, error=_compute_error(error, restype), gil=gil, @@ -562,6 +568,7 @@ '_PyObject_CallFunction_SizeT', '_PyObject_CallMethod_SizeT', 'PyObject_GetBuffer', 'PyBuffer_Release', + '_Py_setfilesystemdefaultencoding', 'PyCObject_FromVoidPtr', 'PyCObject_FromVoidPtrAndDesc', 'PyCObject_AsVoidPtr', 'PyCObject_GetDesc', 'PyCObject_Import', 'PyCObject_SetVoidPtr', @@ -668,7 +675,11 @@ 'PySlice_Type': 'space.gettypeobject(W_SliceObject.typedef)', 'PyStaticMethod_Type': 'space.gettypeobject(StaticMethod.typedef)', 'PyCFunction_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCFunctionObject.typedef)', - 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)', + 'PyClassMethodDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCClassMethodObject.typedef)', + 'PyGetSetDescr_Type': 'space.gettypeobject(cpyext.typeobject.W_GetSetPropertyEx.typedef)', + 'PyMemberDescr_Type': 'space.gettypeobject(cpyext.typeobject.W_MemberDescr.typedef)', + 'PyMethodDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCMethodObject.typedef)', + 'PyWrapperDescr_Type': 'space.gettypeobject(cpyext.methodobject.W_PyCWrapperObject.typedef)', 'PyInstanceMethod_Type': 'space.gettypeobject(cpyext.classobject.InstanceMethod.typedef)', }.items(): register_global(cpyname, 'PyTypeObject*', pypyexpr, header=pypy_decl) @@ -1058,10 +1069,16 @@ get_capsule_type = rffi.llexternal('_%s_get_capsule_type' % prefix, [], PyTypeObjectPtr, compilation_info=eci, _nowrapper=True) + setdefenc = rffi.llexternal('_%s_setfilesystemdefaultencoding' % prefix, + [rffi.CCHARP], lltype.Void, + compilation_info=eci, _nowrapper=True) def init_types(space): from pypy.module.cpyext.typeobject import py_type_ready + from pypy.module.sys.interp_encoding import getfilesystemencoding py_type_ready(space, get_cobject_type()) py_type_ready(space, get_capsule_type()) + s = space.text_w(getfilesystemencoding(space)) + setdefenc(rffi.str2charp(s, track_allocation=False)) # "leaks" INIT_FUNCTIONS.append(init_types) from pypy.module.posix.interp_posix import add_fork_hook global py_fatalerror @@ -1330,6 +1347,21 @@ decls = defaultdict(list) for decl in FORWARD_DECLS: decls[pypy_decl].append("%s;" % (decl,)) + decls[pypy_decl].append(""" +/* hack for https://bugs.python.org/issue29943 */ + +PyAPI_FUNC(int) %s(PyObject *arg0, + Signed arg1, Signed *arg2, + Signed *arg3, Signed *arg4, Signed *arg5); +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif +static int PySlice_GetIndicesEx(PyObject *arg0, Py_ssize_t arg1, + Py_ssize_t *arg2, Py_ssize_t *arg3, Py_ssize_t *arg4, + Py_ssize_t *arg5) { + return %s(arg0, arg1, arg2, arg3, + arg4, arg5); +}""" % ((mangle_name(prefix, 'PySlice_GetIndicesEx'),)*2)) for header_name, header_functions in FUNCTIONS_BY_HEADER.iteritems(): header = decls[header_name] @@ -1638,6 +1670,7 @@ state.fixup_extension(w_mod, name, path) return w_mod +@jit.dont_look_inside def exec_extension_module(space, w_mod): from pypy.module.cpyext.modsupport import exec_def if not space.config.objspace.usemodules.cpyext: diff --git a/pypy/module/cpyext/include/descrobject.h b/pypy/module/cpyext/include/descrobject.h --- a/pypy/module/cpyext/include/descrobject.h +++ b/pypy/module/cpyext/include/descrobject.h @@ -1,34 +1,6 @@ #ifndef Py_DESCROBJECT_H #define Py_DESCROBJECT_H -#define PyDescr_COMMON \ - PyObject_HEAD \ - PyTypeObject *d_type; \ - PyObject *d_name - -typedef struct { - PyDescr_COMMON; -} PyDescrObject; - -typedef struct { - PyDescr_COMMON; - PyMethodDef *d_method; -} PyMethodDescrObject; - -typedef struct { - PyDescr_COMMON; - struct PyMemberDef *d_member; -} PyMemberDescrObject; - -typedef struct { - PyDescr_COMMON; - PyGetSetDef *d_getset; -} PyGetSetDescrObject; - -typedef struct { - PyDescr_COMMON; - struct wrapperbase *d_base; - void *d_wrapped; /* This can be any function pointer */ -} PyWrapperDescrObject; +#include "cpyext_descrobject.h" #endif diff --git a/pypy/module/cpyext/include/fileobject.h b/pypy/module/cpyext/include/fileobject.h --- a/pypy/module/cpyext/include/fileobject.h +++ b/pypy/module/cpyext/include/fileobject.h @@ -1,1 +1,2 @@ -#define Py_FileSystemDefaultEncoding NULL +PyAPI_DATA(const char *) Py_FileSystemDefaultEncoding; +PyAPI_FUNC(void) _Py_setfilesystemdefaultencoding(const char *); diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py --- a/pypy/module/cpyext/methodobject.py +++ b/pypy/module/cpyext/methodobject.py @@ -1,4 +1,5 @@ -from rpython.rtyper.lltypesystem import lltype, rffi, llmemory +from rpython.rtyper.lltypesystem import lltype, rffi +from rpython.rlib import jit from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt @@ -10,8 +11,8 @@ from pypy.module.cpyext.api import ( CONST_STRING, METH_CLASS, METH_COEXIST, METH_KEYWORDS, METH_NOARGS, METH_O, METH_STATIC, METH_VARARGS, PyObject, bootstrap_function, - cpython_api, generic_cpy_call, CANNOT_FAIL, - PyTypeObjectPtr, slot_function, cts) + cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts, + build_type_checkers) from pypy.module.cpyext.pyobject import ( Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr) @@ -42,8 +43,8 @@ from pypy.module.cpyext.object import _dealloc _dealloc(space, py_obj) - class W_PyCFunctionObject(W_Root): + # TODO create a slightly different class depending on the c_ml_flags def __init__(self, space, ml, w_self, w_module=None): self.ml = ml self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, self.ml.c_ml_name)) @@ -56,7 +57,7 @@ w_self = self.w_self flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags) flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) - if space.is_true(w_kw) and not flags & METH_KEYWORDS: + if not flags & METH_KEYWORDS and space.is_true(w_kw): raise oefmt(space.w_TypeError, "%s() takes no keyword arguments", self.name) @@ -89,6 +90,20 @@ else: return space.w_None +class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject): + def call(self, space, w_self, w_args, w_kw): + # Call the C function + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, None) + +class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject): + def call(self, space, w_self, w_o, w_kw): + if w_self is None: + w_self = self.w_self + func = self.ml.c_ml_meth + return generic_cpy_call(space, func, w_self, w_o) class W_PyCMethodObject(W_PyCFunctionObject): w_self = None @@ -102,7 +117,7 @@ return self.space.unwrap(self.descr_method_repr()) def descr_method_repr(self): - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) return self.space.newtext("<method '%s' of '%s' objects>" % ( self.name, w_objclass.name)) @@ -110,7 +125,7 @@ def descr_call(self, space, __args__): args_w, kw_w = __args__.unpack() if len(args_w) < 1: - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) raise oefmt(space.w_TypeError, "descriptor '%8' of '%s' object needs an argument", @@ -118,7 +133,7 @@ w_instance = args_w[0] # XXX: needs a stricter test if not space.isinstance_w(w_instance, self.w_objclass): - w_objclass = self.w_objclass + w_objclass = self.w_objclass assert isinstance(w_objclass, W_TypeObject) raise oefmt(space.w_TypeError, "descriptor '%8' requires a '%s' object but received a '%T'", @@ -130,6 +145,10 @@ ret = self.call(space, w_instance, w_args, w_kw) return ret +# PyPy addition, for Cython +_, _ = build_type_checkers("MethodDescr", W_PyCMethodObject) + + @cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL) def PyCFunction_Check(space, w_obj): from pypy.interpreter.function import BuiltinFunction @@ -156,6 +175,7 @@ (self.name.decode('utf-8'), self.w_objclass.getname(self.space))) + class W_PyCWrapperObject(W_Root): def __init__(self, space, pto, method_name, wrapper_func, wrapper_func_kwds, doc, func, offset=None): @@ -203,6 +223,7 @@ (self.method_name, self.w_objclass.name)) +@jit.dont_look_inside def cwrapper_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCWrapperObject, w_self) args_w, kw_w = __args__.unpack() @@ -213,10 +234,22 @@ space.setitem(w_kw, space.newtext(key), w_obj) return self.call(space, w_self, w_args, w_kw) +def cfunction_descr_call_noargs(space, w_self): + # special case for calling with flags METH_NOARGS + self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self) + return self.call(space, None, None, None) +def cfunction_descr_call_single_object(space, w_self, w_o): + # special case for calling with flags METH_O + self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self) + return self.call(space, None, w_o, None) + +@jit.dont_look_inside def cfunction_descr_call(space, w_self, __args__): + # specialize depending on the W_PyCFunctionObject self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() + # XXX __args__.unpack is slow w_args = space.newtuple(args_w) w_kw = space.newdict() for key, w_obj in kw_w.items(): @@ -224,6 +257,7 @@ ret = self.call(space, None, w_args, w_kw) return ret +@jit.dont_look_inside def cclassmethod_descr_call(space, w_self, __args__): self = space.interp_w(W_PyCFunctionObject, w_self) args_w, kw_w = __args__.unpack() @@ -261,6 +295,26 @@ ) W_PyCFunctionObject.typedef.acceptable_as_base_class = False +W_PyCFunctionObjectNoArgs.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_noargs), + __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False + +W_PyCFunctionObjectSingleObject.typedef = TypeDef( + 'builtin_function_or_method', W_PyCFunctionObject.typedef, + __call__ = interp2app(cfunction_descr_call_single_object), + __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc), + __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject), + __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject, + wrapfn="newtext_or_none"), + ) +W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False + W_PyCMethodObject.typedef = TypeDef( 'method_descriptor', __get__ = interp2app(cmethod_descr_get), @@ -323,11 +377,15 @@ def PyClassMethod_New(space, w_func): return ClassMethod(w_func) -@cpython_api([PyTypeObjectPtr, lltype.Ptr(PyMethodDef)], PyObject) +@cts.decl(""" + PyObject * + PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""") def PyDescr_NewMethod(space, w_type, method): return W_PyCMethodObject(space, method, w_type) -@cpython_api([PyObject, lltype.Ptr(PyMethodDef)], PyObject) +@cts.decl(""" + PyObject * + PyDescr_NewClassMethod(PyTypeObject *type, PyMethodDef *method)""") def PyDescr_NewClassMethod(space, w_type, method): return W_PyCClassMethodObject(space, method, w_type) diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py --- a/pypy/module/cpyext/modsupport.py +++ b/pypy/module/cpyext/modsupport.py @@ -1,13 +1,15 @@ from rpython.rtyper.lltypesystem import rffi, lltype from pypy.module.cpyext.api import ( cpython_api, METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, cts, + METH_NOARGS, METH_O, parse_dir, bootstrap_function, generic_cpy_call, generic_cpy_call_dont_convert_result, slot_function) from pypy.module.cpyext.pyobject import PyObject, as_pyobj, make_typedescr from pypy.interpreter.module import Module from pypy.module.cpyext.methodobject import ( W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod, - PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New) + PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New, + W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject) from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall from pypy.module.cpyext.state import State from pypy.interpreter.error import oefmt @@ -165,6 +167,13 @@ "exception", w_mod.w_name) cur_slot = rffi.ptradd(cur_slot, 1) +def _create_pyc_function_object(space, method, w_self, w_name, flags): + flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST) + if flags == METH_NOARGS: + return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name) + if flags == METH_O: + return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name) + return W_PyCFunctionObject(space, method, w_self, w_name) def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None): w_name = space.newtext_or_none(name) @@ -184,7 +193,8 @@ raise oefmt(space.w_ValueError, "module functions cannot set METH_CLASS or " "METH_STATIC") - w_obj = W_PyCFunctionObject(space, method, w_self, w_name) + w_obj = _create_pyc_function_object(space, method, w_self, + w_name, flags) else: if methodname in dict_w and not (flags & METH_COEXIST): continue diff --git a/pypy/module/cpyext/parse/cpyext_descrobject.h b/pypy/module/cpyext/parse/cpyext_descrobject.h new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/parse/cpyext_descrobject.h @@ -0,0 +1,29 @@ +typedef struct { + PyObject_HEAD + PyTypeObject *d_type; + PyObject *d_name; + PyObject *d_qualname; +} PyDescrObject; + +#define PyDescr_COMMON PyDescrObject d_common + +typedef struct { + PyDescr_COMMON; + PyMethodDef *d_method; +} PyMethodDescrObject; + +typedef struct { + PyDescr_COMMON; + struct PyMemberDef *d_member; +} PyMemberDescrObject; + +typedef struct { + PyDescr_COMMON; + PyGetSetDef *d_getset; +} PyGetSetDescrObject; + +typedef struct { + PyDescr_COMMON; + struct wrapperbase *d_base; + void *d_wrapped; /* This can be any function pointer */ +} PyWrapperDescrObject; diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py --- a/pypy/module/cpyext/pyobject.py +++ b/pypy/module/cpyext/pyobject.py @@ -14,7 +14,7 @@ from rpython.rlib.objectmodel import specialize from rpython.rlib.objectmodel import keepalive_until_here from rpython.rtyper.annlowlevel import llhelper -from rpython.rlib import rawrefcount +from rpython.rlib import rawrefcount, jit from rpython.rlib.debug import fatalerror @@ -151,6 +151,7 @@ class InvalidPointerException(Exception): pass +@jit.dont_look_inside def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given @@ -190,6 +191,7 @@ w_marker_deallocating = W_Root() +@jit.dont_look_inside def from_ref(space, ref): """ Finds the interpreter object corresponding to the given reference. If the @@ -227,6 +229,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) +@jit.dont_look_inside def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. 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 @@ -420,23 +420,6 @@ from rpython.rlib.nonconst import NonConstant -SLOTS = {} - -@specialize.memo() -def get_slot_tp_function(space, typedef, name): - """Return a description of the slot C function to use for the built-in - type for 'typedef'. The 'name' is the slot name. This is a memo - function that, after translation, returns one of a built-in finite set. - """ - key = (typedef, name) - try: - return SLOTS[key] - except KeyError: - slot_func = build_slot_tp_function(space, typedef, name) - api_func = slot_func.api_func if slot_func else None - SLOTS[key] = api_func - return api_func - def build_slot_tp_function(space, typedef, name): w_type = space.gettypeobject(typedef) @@ -785,7 +768,7 @@ def __init__(self, method_name, slot_name, function, wrapper1, wrapper2, doc): self.method_name = method_name self.slot_name = slot_name - self.slot_names = ("c_" + slot_name).split(".") + self.slot_names = tuple(("c_" + slot_name).split(".")) self.slot_func = function self.wrapper_func = wrapper1 self.wrapper_func_kwds = wrapper2 diff --git a/pypy/module/cpyext/src/missing.c b/pypy/module/cpyext/src/missing.c --- a/pypy/module/cpyext/src/missing.c +++ b/pypy/module/cpyext/src/missing.c @@ -27,3 +27,7 @@ int Py_Py3kWarningFlag = 0; int Py_HashRandomizationFlag = 0; +const char *Py_FileSystemDefaultEncoding; /* filled when cpyext is imported */ +void _Py_setfilesystemdefaultencoding(const char *enc) { + Py_FileSystemDefaultEncoding = enc; +} diff --git a/pypy/module/cpyext/test/foo.c b/pypy/module/cpyext/test/foo.c --- a/pypy/module/cpyext/test/foo.c +++ b/pypy/module/cpyext/test/foo.c @@ -83,6 +83,35 @@ return cls; } +// for CPython +#ifndef PyMethodDescr_Check +int PyMethodDescr_Check(PyObject* method) +{ + PyObject *meth = PyObject_GetAttrString((PyObject*)&PyList_Type, "append"); + if (!meth) return 0; + int res = PyObject_TypeCheck(method, meth->ob_type); + Py_DECREF(meth); + return res; +} +#endif + +PyObject* make_classmethod(PyObject* method) +{ + // adapted from __Pyx_Method_ClassMethod + if (PyMethodDescr_Check(method)) { + PyMethodDescrObject *descr = (PyMethodDescrObject *)method; + PyTypeObject *d_type = descr->d_common.d_type; + return PyDescr_NewClassMethod(d_type, descr->d_method); + } + else if (PyMethod_Check(method)) { + return PyClassMethod_New(PyMethod_GET_FUNCTION(method)); + } + else { + PyErr_SetString(PyExc_TypeError, "unknown method kind"); + return NULL; + } +} + static PyObject * foo_unset(fooobject *self) { @@ -95,6 +124,7 @@ {"copy", (PyCFunction)foo_copy, METH_NOARGS, NULL}, {"create", (PyCFunction)foo_create, METH_NOARGS|METH_STATIC, NULL}, {"classmeth", (PyCFunction)foo_classmeth, METH_NOARGS|METH_CLASS, NULL}, + {"fake_classmeth", (PyCFunction)foo_classmeth, METH_NOARGS, NULL}, {"unset_string_member", (PyCFunction)foo_unset, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; @@ -167,19 +197,19 @@ /* copied from numpy scalartypes.c for inherited classes */ if (t->tp_bases && (PyTuple_GET_SIZE(t->tp_bases) > 1)) { - PyTypeObject *sup; - /* We are inheriting from a Python type as well so + PyTypeObject *sup; + /* We are inheriting from a Python type as well so give it first dibs on conversion */ sup = (PyTypeObject *)PyTuple_GET_ITEM(t->tp_bases, 1); - /* Prevent recursion */ - if (new_fooType != sup->tp_new) + /* Prevent recursion */ + if (new_fooType != sup->tp_new) { o = sup->tp_new(t, args, kwds); return o; } } o = t->tp_alloc(t, 0); - return o; + return o; }; static PyMemberDef foo_members[] = { @@ -717,7 +747,7 @@ "foo", "Module Doc", -1, - foo_functions, + foo_functions, NULL, NULL, NULL, @@ -751,6 +781,7 @@ #endif { PyObject *d; + PyObject *fake_classmeth, *classmeth; #if PY_MAJOR_VERSION >= 3 PyObject *module = PyModule_Create(&moduledef); #else @@ -808,6 +839,12 @@ INITERROR; gettype2 = PyObject_New(PyObject, &GetType2); + fake_classmeth = PyDict_GetItemString((PyObject *)fooType.tp_dict, "fake_classmeth"); + classmeth = make_classmethod(fake_classmeth); + if (classmeth == NULL) + INITERROR; + if (PyDict_SetItemString((PyObject *)fooType.tp_dict, "fake_classmeth", classmeth) < 0) + INITERROR; d = PyModule_GetDict(module); if (d == NULL) diff --git a/pypy/module/cpyext/test/test_fileobject.py b/pypy/module/cpyext/test/test_fileobject.py new file mode 100644 --- /dev/null +++ b/pypy/module/cpyext/test/test_fileobject.py @@ -0,0 +1,13 @@ +from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase + + +class AppTestFileObject(AppTestCpythonExtensionBase): + def test_defaultencoding(self): + import sys + module = self.import_extension('foo', [ + ("defenc", "METH_NOARGS", + """ + return PyUnicode_FromString(Py_FileSystemDefaultEncoding); + """), + ]) + assert module.defenc() == sys.getfilesystemencoding() 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 @@ -72,7 +72,7 @@ """ /* Create an approximation of the buffer for a 0d ndarray */ Py_buffer buf; - PyObject *str = PyBytes_FromString("hello, world."); + PyObject *ret, *str = PyBytes_FromString("hello, world."); buf.buf = PyBytes_AsString(str); buf.obj = str; buf.readonly = 1; @@ -80,7 +80,7 @@ buf.itemsize = 13; buf.ndim = 0; buf.shape = NULL; - PyObject* ret = PyMemoryView_FromBuffer(&buf); + ret = PyMemoryView_FromBuffer(&buf); return ret; """)]) result = module.create_view() diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py --- a/pypy/module/cpyext/test/test_typeobject.py +++ b/pypy/module/cpyext/test/test_typeobject.py @@ -135,6 +135,12 @@ "<method 'copy' of 'foo' objects>") raises(TypeError, descr, None) + def test_cython_fake_classmethod(self): + module = self.import_module(name='foo') + print(module.fooType.fake_classmeth) + print(type(module.fooType.fake_classmeth)) + assert module.fooType.fake_classmeth() is module.fooType + def test_new(self): # XXX cpython segfaults but if run singly (with -k test_new) this passes module = self.import_module(name='foo') @@ -1315,6 +1321,52 @@ assert Asize == Bsize assert Asize > basesize + def test_multiple_inheritance_bug1(self): + module = self.import_extension('foo', [ + ("get_type", "METH_NOARGS", + ''' + Py_INCREF(&Foo_Type); + return (PyObject *)&Foo_Type; + ''' + ), ("forty_two", "METH_O", + ''' + return PyLong_FromLong(42); + ''' + )], prologue=''' + static PyTypeObject Foo_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "foo.foo", + }; _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit